home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
System Booster
/
System Booster.iso
/
Archives
/
GNU
/
GNUPLOTsrc.lha
/
graphics.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-22
|
102KB
|
3,631 lines
#ifndef lint
static char *RCSid = "$Id: graphics.c,v 1.93 1995/12/20 22:39:34 drd Exp $";
#endif
/* GNUPLOT - graphics.c */
/*
* Copyright (C) 1986 - 1993 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the modified code. Modifications are to be distributed
* as patches to released version.
*
* This software is provided "as is" without express or implied warranty.
*
*
* AUTHORS
*
* Original Software:
* Thomas Williams, Colin Kelley.
*
* Gnuplot 2.0 additions:
* Russell Lang, Dave Kotz, John Campbell.
*
* Gnuplot 3.0 additions:
* Gershon Elber and many others.
*
* There is a mailing list for gnuplot users. Note, however, that the
* newsgroup
* comp.graphics.gnuplot
* is identical to the mailing list (they
* both carry the same set of messages). We prefer that you read the
* messages through that newsgroup, to subscribing to the mailing list.
* (If you can read that newsgroup, and are already on the mailing list,
* please send a message info-gnuplot-request@dartmouth.edu, asking to be
* removed from the mailing list.)
*
* The address for mailing to list members is
* info-gnuplot@dartmouth.edu
* and for mailing administrative requests is
* info-gnuplot-request@dartmouth.edu
* The mailing list for bug reports is
* bug-gnuplot@dartmouth.edu
* The list of those interested in beta-test versions is
* info-gnuplot-beta@dartmouth.edu
*/
#include <math.h>
#include <assert.h>
#include <ctype.h>
#include "plot.h"
#include "setshow.h"
/* key placement is calculated in boundary, so we need file-wide variables
* To simplify adjustments to the key, we set all these once [depends on
* key_reverse] and use them throughout.
*/
/*{{{ local and global variables*/
static int key_sample_left; /* offset from x for left of line sample */
static int key_sample_right; /* offset from x for right of line sample */
static int key_point_offset; /* offset from x for point sample */
static int key_text_left; /* offset from x for left-justified text */
static int key_text_right; /* offset from x for right-justified text */
static int key_size_left; /* size of left bit of key (text or sample, depends on key_reverse) */
static int key_size_right; /* size of right part of key (including padding) */
/* I think the following should also be static ?? */
static int key_xl, key_xr, key_yt, key_yb; /* boundarys for key field */
static int max_ptitl_len = 0; /* max length of plot-titles (keys) */
static int ktitl_lines = 0; /* no lines in key_title (key header) */
static int ptitl_cnt; /* count keys with len > 0 */
static int key_cols; /* no cols of keys */
static int key_rows, key_col_wth, yl_ref;
/* penalty for doing tics by callback in gen_tics is need for
* global variables to communicate with the tic routines
* Dont need to be arrays for this
*/
static int tic_start, tic_direction, tic_text, tic_just, tic_mirror;
/* set by tic_callback - how large to draw polar radii */
static double largest_polar_circle;
/* either xformat etc or invented time format
* index with FIRST_X_AXIS etc
* global because used in gen_tics, which graph3d also uses
*/
char ticfmt[8][25];
int timelevel[8];
double ticstep[8];
static int xlablin, x2lablin, ylablin, y2lablin, titlelin, xticlin, x2ticlin;
static unsigned int x2ticdelta; /* 0 for tic_in or no x2tics, ticscale*v_tic otherwise */
static int top_margin; /* in lines, excluding x2tics */
static int key_entry_height; /* bigger of t->v_size, pointsize*t->v_tick */
static int p_width, p_height; /* pointsize * { t->h_tic | t->v_tic } */
#if defined(DJGPP)||defined(sun386)
#define time_t unsigned long
#endif
/* there are several things on right of plot - key, y2tics and y2label
* when working out boundary, save posn of y2label for later...
* Same goes for x2label.
* key posn is also stored in key_xl, and tics go at xright
*/
static int y2label_x;
/*}}}*/
/*{{{ static fns and local macros*/
static void plot_impulses __P((struct curve_points *plot, int yaxis_x, int xaxis_y));
static void plot_lines __P((struct curve_points *plot));
static void plot_points __P((struct curve_points *plot));
static void plot_dots __P((struct curve_points *plot));
static void plot_bars __P((struct curve_points *plot));
static void plot_boxes __P((struct curve_points *plot, int xaxis_y));
static void plot_vectors __P((struct curve_points *plot));
static void edge_intersect __P((struct coordinate GPHUGE *points, int i, double *ex, double *ey));
static int two_edge_intersect __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));
static TBOOLEAN two_edge_intersect_steps __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));
static void plot_steps __P((struct curve_points *plot)); /* JG */
static void plot_fsteps __P((struct curve_points *plot)); /* HOE */
static void edge_intersect_steps __P((struct coordinate GPHUGE *points, int i, double *ex, double *ey)); /* JG */
static void edge_intersect_fsteps __P((struct coordinate GPHUGE *points, int i, double *ex, double *ey)); /* HOE */
static TBOOLEAN two_edge_intersect_steps __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly)); /* JG */
static TBOOLEAN two_edge_intersect_fsteps __P((struct coordinate GPHUGE *points, int i, double *lx, double *ly));
static double LogScale __P((double coord, int is_log, double log_base_log, char *what, char *axis));
static double dbl_raise __P((double x, int y));
static void boundary __P((int scaling, struct curve_points *plots, int count));
static double set_tic __P((double l10, int guide));
static double make_tics __P((int axis, int guide));
static int widest_tic; /* widest2d_callback keeps longest so far in here */
static void widest2d_callback __P((int axis, double place, char *text, int grid));
static void ytick2d_callback __P((int axis, double place, char *text, int grid));
static void xtick2d_callback __P((int axis, double place, char *text, int grid));
static void map_position __P((struct position *pos, unsigned int *x, unsigned int *y, char *what));
#if defined(sun386) || defined(AMIGA_SC_6_1)
static double CheckLog __P((TBOOLEAN is_log, double base_log, double x));
#endif
/* for plotting error bars */
#define ERRORBARTIC (t->h_tic/2) /* half the width of error bar tic mark */
#ifndef max /* Lattice C has max() in math.h, but shouldn't! */
#define max(a,b) ((a > b) ? a : b)
#endif
#ifndef min
#define min(a,b) ((a < b) ? a : b)
#endif
/*
* The Amiga SAS/C 6.2 compiler moans about macro envocations causing
* multiple calls to functions. I converted these macros to inline
* functions coping with the problem without loosing speed.
* If your compiler supports __inline, you should add it to the
* #ifdef directive
* (MGR, 1993)
*/
#ifdef AMIGA_SC_6_1
__inline static TBOOLEAN i_inrange(int z,int min,int max)
{
return((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)));
}
__inline static double f_max(double a,double b)
{
return(max(a,b));
}
__inline static double f_min(double a,double b)
{
return(min(a,b));
}
#else
#define f_max(a,b) max((a),(b))
#define f_min(a,b) min((a),(b))
#define i_inrange(z,a,b) inrange((z),(a),(b))
#endif
#define inrange(z,min,max) ((min<max) ? ((z>=min)&&(z<=max)) : ((z>=max)&&(z<=min)) )
/* True if a and b have the same sign or zero (positive or negative) */
#define samesign(a,b) ((a) * (b) >= 0)
/*}}}*/
/*{{{ more variables*/
/* Define the boundary of the plot
* These are computed at each call to do_plot, and are constant over
* the period of one do_plot. They actually only change when the term
* type changes and when the 'set size' factors change.
* - no longer true, for 'set key out' or 'set key under'. also depend
* on tic marks and multi-line labels.
* They are shared with graph3d.c since we want to use its draw_clip_line()
*/
int xleft, xright, ybot, ytop;
/* we make a local copy of the 'key' variable so that if something
* goes wrong, we can switch it off temporarily
*/
static int lkey;
/* First attempt at double axes...
* x_min etc are now accessed from a global array min_array[], max_array[]
* put the scale factors into a similar array
* for convenience in this first attack on double axes, just define x_min etc
* since code already uses x_min, etc Eventually it will be done properly
*/
extern TBOOLEAN term_graphics, term_suspended;
extern double min_array[], max_array[];
extern int auto_array[];
extern int log_array[];
extern double base_array[], log_base_array[];
static int x_axis=FIRST_X_AXIS, y_axis=FIRST_Y_AXIS; /* current axes */
static double scale[AXIS_ARRAY_SIZE]; /* scale factors for mapping for each axis */
/* BODGES BEFORE I FIX IT UP */
#define x_min min_array[x_axis]
#define x_max max_array[x_axis]
#define y_min min_array[y_axis]
#define y_max max_array[y_axis]
/* And the functions to map from user to terminal coordinates */
#define map_x(x) (int)(xleft+(x-min_array[x_axis])*scale[x_axis]+0.5) /* maps floating point x to screen */
#define map_y(y) (int)(ybot +(y-min_array[y_axis])*scale[y_axis]+0.5) /* same for y */
/* (DFK) Watch for cancellation error near zero on axes labels */
#define SIGNIF (0.01) /* less than one hundredth of a tic mark */
#define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
#define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
/*}}}*/
/*{{{ CheckLog()*/
/* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog
* macro, so I write it as a function on that machine.
*
* Amiga SAS/C 6.2 thinks it will do too much work calling functions in
* macro arguments twice, thus I inline theese functions. (MGR, 1993)
* If your compiler doesn't handle those macros correctly, you should
* also subscribe here. Even without inlining you gain speed with log plots
*/
#if defined(sun386) || defined(AMIGA_SC_6_1)
#ifdef AMIGA_SC_6_1
__inline
#endif
static double
CheckLog(is_log, base_log, x)
TBOOLEAN is_log;
double base_log;
double x;
{
if (is_log)
return(pow(base_log, x));
else
return(x);
}
#else
/* (DFK) Use 10^x if logscale is in effect, else x */
#define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
#endif /* sun386 || SAS/C */
/*}}}*/
/*{{{ LogScale()*/
static double
LogScale(coord, is_log, log_base_log, what, axis)
double coord; /* the value */
TBOOLEAN is_log; /* is this axis in logscale? */
double log_base_log; /* if so, the log of its base */
char *what; /* what is the coord for? */
char *axis; /* which axis is this for ("x" or "y")? */
{
if (is_log) {
if (coord <= 0.0) {
char errbuf[100]; /* place to write error message */
(void) sprintf(errbuf,"%s has %s coord of %g; must be above 0 for log scale!",
what, axis, coord);
graph_error(errbuf);
} else
return(log(coord)/log_base_log);
}
return(coord);
}
/*}}}*/
/*{{{ graph_error()*/
/* handle errors during graph-plot in a consistent way */
void graph_error(text)
char *text;
{
if (term_graphics) {
if (multiplot && term_suspended && term->resume)
(*term->resume)(); /* some drivers may rely on this ? */
(term->text)();
fflush(outfile);
term_graphics = FALSE;
term_suspended= FALSE;
multiplot = FALSE;
}
int_error(text, NO_CARET);
}
/*}}}*/
/*{{{ widest2d_callback()*/
/* we determine widest tick label by getting gen_ticks to call this
* routine with every label
*/
static void widest2d_callback(axis, place, text, grid)
int axis;
double place;
char *text;
int grid;
{
int len=label_width(text, NULL);
if (len>widest_tic)
widest_tic = len;
}
/*}}}*/
/*{{{ boundary()*/
/* borders of plotting area
* computed once on every call to do_plot
*
* The order in which things is done is getting pretty critical:
* ytop depends on title, x2label, ylabels (if no rotated text)
* ybot depends on key, if TUNDER
* once we have these, we can setup the y1 and y2 tics and the
* only then can we calculate xleft and xright
* xright depends also on key TRIGHT
* then we can do x and x2 tics
*
* For set size square, everything depends on everything else...
* not really a lot we can do about that, so we lose if the plot has to
* be reduced vertically. But the chances are the
* change will not be very big, so the number of tics will not
* change dramatically.
*/
static void boundary(scaling,plots,count)
TBOOLEAN scaling; /* TRUE if terminal is doing the scaling */
struct curve_points *plots;
int count;
{
int ytlen;
register struct termentry *t = term;
int key_h, key_w;
int can_rotate=(*t->text_angle)(1);
int yticlin=0, y2ticlin=0, timelin=0;
lkey = key; /* but we may have to disable it later */
xticlin = ylablin = y2lablin = xlablin = x2lablin = titlelin = top_margin = 0;
/*{{{ count lines in labels and tics*/
if (*title.text) label_width(title.text, &titlelin);
if (*xlabel.text) label_width(xlabel.text, &xlablin);
if (*x2label.text) label_width(x2label.text, &x2lablin);
if (*ylabel.text) label_width(ylabel.text, &ylablin);
if (*y2label.text) label_width(y2label.text, &y2lablin);
if (xtics) label_width(xformat, &xticlin);
if (x2tics) label_width(x2format, &x2ticlin);
if (ytics) label_width(yformat, &yticlin);
if (y2tics) label_width(y2format, &y2ticlin);
if (*timelabel.text) label_width(timelabel.text, &timelin);
/*}}}*/
/*{{{ ytop*/
if (tmargin >= 0)
top_margin = tmargin;
else {
/* allow an extra one if both are present */
top_margin = titlelin + x2lablin + (titlelin && x2lablin);
if (*ylabel.text && !can_rotate && ylablin>top_margin)
top_margin = ylablin;
if (*y2label.text && !can_rotate && y2lablin>top_margin)
top_margin = y2lablin;
}
if (x2tics&TICS_ON_BORDER) {
/* ought to consider tics on axes if axis near border */
top_margin += x2ticlin+0.5;
}
if (tmargin < 0 && !tic_in && (x2tics&TICS_ON_BORDER || xtics&TICS_MIRROR))
x2ticdelta = (t->v_tic)*ticscale;
else
x2ticdelta = 0;
ytop = (ysize+yoffset) * (t->ymax) - (t->v_char)*(top_margin+1) - x2ticdelta;
/*}}}*/
/*{{{ tentative xleft, needed for TUNDER*/
xleft = xoffset * (t->xmax) + (lmargin > 0 ? (t->h_char)*lmargin : t->h_tic);
/*}}}*/
/*{{{ tentative xright, needed for TUNDER*/
if (rmargin >= 0)
xright = (xsize+xoffset) * (t->xmax) - (t->h_char)*rmargin;
else
xright = (xsize+xoffset) * (t->xmax) - (t->h_char)*2 - (t->h_tic);
/*}}}*/
/*{{{ ybot*/
ybot = t->ymax * yoffset;
if (bmargin >= 0)
ybot += bmargin * (t->v_char);
else {
if (xtics&TICS_ON_BORDER)
ybot += (t->v_char)*(xticlin+0.5);
else
ybot += (t->v_char)*0.5;
if (*xlabel.text || (!can_rotate && *timelabel.text))
{
ybot += (t->v_char)*(((!can_rotate && (timelin > xlablin)) ? timelin : xlablin)+0.5);
}
if (!tic_in && (xtics&TICS_ON_BORDER || x2tics&TICS_MIRROR))
ybot += (t->v_tic)*ticscale;
}
/*}}}*/
#define KEY_PANIC(x) if (x) { lkey=0; goto key_escape; }
if (lkey) {
/*{{{ essential key features*/
p_width = pointsize * t->h_tic;
p_height = pointsize * t->v_tic;
key_entry_height = p_height * 1.25;
if (key_entry_height < (t->v_char) )
key_entry_height = (t->v_char);
/* count max_len key and number keys with len > 0 */
max_ptitl_len = find_maxl_keys(plots,count,&ptitl_cnt);
if ( (ytlen = label_width(key_title,&ktitl_lines)) > max_ptitl_len )
max_ptitl_len = ytlen;
if (key_reverse) {
key_sample_left= -4*(t->h_char) - p_width;
key_sample_right= 0;
key_text_left=t->h_char;
key_text_right=(t->h_char)*(max_ptitl_len+1);
key_size_right=key_text_right + t->h_char;
key_size_left=t->h_char - key_sample_left; /* sample left is -ve */
key_size_right=key_text_right + t->h_char;
} else {
key_sample_left= 0;
key_sample_right= 4*(t->h_char) + p_width;
key_text_left=-(t->h_char)*(max_ptitl_len+1);
key_text_right=-(t->h_char);
key_size_left=t->h_char - key_text_left;
key_size_right=key_sample_right + t->h_char;
}
key_point_offset = (key_sample_left + key_sample_right)/2;
/* advance width for cols */
key_col_wth = key_size_left + key_size_right;
key_rows = ptitl_cnt;
key_cols = 1;
/* calculate rows and cols for key - if something goes wrong,
* the tidiest way out is to set lkey=0, and a goto
*/
if (lkey == -1) {
if (key_vpos == TUNDER ) {
/* maximise no cols, limited by label-length */
key_cols = (int)(xright - xleft)/key_col_wth;
KEY_PANIC(key_cols == 0);
key_rows = (int)(ptitl_cnt+key_cols-1)/key_cols;
KEY_PANIC(key_rows==0);
/* now calculate actual no cols depending on no rows */
key_cols = (int)(ptitl_cnt+key_rows-1)/key_rows;
KEY_PANIC(key_cols==0);
key_col_wth = (int)(xright - xleft)/key_cols;
/* we divide into columns, then centre in column by considering ratio
* of key_left_size to key_right_size
* key_size_left/(key_size_left+key_size_right) * (xright-xleft)/key_cols
* do one integer division to maximise accuracy (hope we dont overflow !)
*/
key_xl = xleft - key_size_left + ((xright-xleft)*key_size_left) / (key_cols*(key_size_left+key_size_right));
key_xr = key_xl + key_col_wth * (key_cols-1) + key_size_left + key_size_right;
key_yb = t->ymax * yoffset;
key_yt = key_yb + key_rows * key_entry_height + ktitl_lines * t->v_char;
ybot += key_entry_height*key_rows + t->v_char*(ktitl_lines+1);
} else {
/* maximise no rows, limited by ytop-ybot */
int i = (int)(ytop-ybot-(ktitl_lines+1)*(t->v_char))/key_entry_height;
KEY_PANIC(i==0);
if (ptitl_cnt > i) {
key_cols = (int)(ptitl_cnt+i-1)/i;
/* now calculate actual no rows depending on no cols */
KEY_PANIC(key_cols==0);
key_rows = (int)(ptitl_cnt+key_cols-1)/key_cols;
}
}
/* come here if we detect a division by zero in key calculations */
key_escape:
; /* ansi requires this */
}
/*}}}*/
}
/*{{{ set up y and y2 tics*/
{
int guide = (ytop-ybot)/term->v_char;
if (ytics) setup_tics(FIRST_Y_AXIS, &yticdef, yformat, (int) (guide/yticlin));
if (y2tics) setup_tics(SECOND_Y_AXIS, &y2ticdef, y2format, (int) (guide/y2ticlin));
}
/*}}}*/
/*{{{ fix up xleft based on widest ytic, ylabels, etc*/
if ( (lmargin == -1)) {
if (ytics&TICS_ON_BORDER) {
widest_tic=0; /* reset the global variable ... */
/* get gen_tics to call widest2d_callback with all labels
* the latter sets widest_tic to the length of the widest one
* ought to consider tics on axis if axis near border...
*/
gen_tics(FIRST_Y_AXIS, &yticdef, 0, 0, 0.0, widest2d_callback);
xleft += (t->h_char)*(widest_tic+3);
}
if (*ylabel.text || (can_rotate && *timelabel.text)) {
xleft += (t->v_char)*(can_rotate && timelin>ylablin ? timelin : ylablin);
}
if (!tic_in && (ytics&TICS_ON_BORDER || y2tics&TICS_MIRROR))
xleft += (t->h_tic)*ticscale;
}
/*}}}*/
/*{{{ fix up xright based on widest y2tic. y2labels, key TOUT*/
if ( rmargin<0 && lkey == -1 && key_hpos == TOUT ) {
xright -= key_col_wth*key_cols;
key_xl = xright + (t->h_tic);
}
/* fix up xright some more if y2label
* also, store y2label posn, since it is not trivial to
* work out again
*/
if ( *y2label.text) {
if (can_rotate && rmargin<0)
xright -= (t->v_char)*y2lablin;
/* else it goes at top, so we dont need to move xright */
y2label_x = xright; /* right or top justified to here */
}
/* fix up xright yet more if second tics */
if (rmargin<0) {
if (y2tics&TICS_ON_BORDER) {
/* dont use map_x/map_y, so dont need to set x_axis/y_axis */
widest_tic=0; /* reset the global variable */
gen_tics(SECOND_Y_AXIS, &y2ticdef, 0, 0, 0.0, widest2d_callback);
xright -= (widest_tic+1) * (t->h_char);
}
if (!tic_in && (y2tics&TICS_ON_BORDER || ytics&TICS_MIRROR))
xright -= (t->h_tic)*ticscale;
}
/*}}}*/
/*{{{ set aspect ratio if required*/
if (square) {
double current = ( (double) (ytop-ybot)) / ( (double) (xright-xleft) );
double required= ( (double) t->v_tic) / ( (double) t->h_tic);
if (current > required) {
/* too tall */
ytop = ybot + required * (xright-xleft);
} else {
int y2 = y2label_x - xright;
xright = xleft + (ytop-ybot)/required;
y2label_x = xright + y2;
}
}
/*}}}*/
/*{{{ set up x and x2 tics*/
/* we should base the guide on the width of the xtics, but we cannot
* use widest_tics until tics are set up. Bit of a downer - let us
* assume tics are 5 characters wide
*/
{
int guide = (xright - xleft) / (5*t->h_char);
if (xtics) setup_tics(FIRST_X_AXIS, &xticdef, xformat, guide);
if (x2tics) setup_tics(SECOND_X_AXIS, &x2ticdef, x2format, guide);
}
/*}}}*/
/* restore text to horizontal [we tested rotation above] */
(void)(*t->text_angle)(0);
/* needed for map_position() below */
scale[FIRST_Y_AXIS] = (ytop - ybot)/(max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
scale[FIRST_X_AXIS] = (xright - xleft)/(max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
scale[SECOND_Y_AXIS] = (ytop - ybot)/(max_array[SECOND_Y_AXIS] - min_array[SECOND_Y_AXIS]);
scale[SECOND_X_AXIS] = (xright - xleft)/(max_array[SECOND_X_AXIS] - min_array[SECOND_X_AXIS]);
/*{{{ calculate the window in the grid for the key*/
if (lkey==1 || (lkey==-1 && key_vpos != TUNDER)) {
/* calculate space for keys to prevent grid overwrite the keys */
/* do it even if there is no grid, as do_plot will use these to position key */
key_w = key_col_wth*key_cols;
key_h = (ktitl_lines) * t->v_char + key_rows * key_entry_height;
if (lkey == -1) {
if ( key_vpos == TTOP ) {
key_yt = (int) ytop - (t->v_tic);
key_yb = key_yt - key_h;
} else {
key_yb = ybot + (t->v_tic);
key_yt = key_yb + key_h;
}
if ( key_hpos == TLEFT ) {
key_xl = xleft + (t->h_char); /* for Left just */
key_xr = key_xl + key_w;
} else if (key_hpos == TRIGHT) {
key_xr = xright - (t->h_char); /* for Right just */
key_xl = key_xr - key_w;
} else /* TOUT */ {
/* do this here for do_plot() */
/* align right first since rmargin may be manual */
key_xr = (xsize+xoffset) * (t->xmax) - (t->h_char);
key_xl = key_xr - key_w;
}
} else {
unsigned int x,y;
map_position(&key_user_pos, &x, &y, "key");
key_xl = x - key_size_left;
key_xr = key_xl + key_w;
key_yt = y + (ktitl_lines ? t->v_char : key_entry_height)/2;
key_yb = key_yt - key_h;
}
}
/*}}}*/
}
/*}}}*/
/*{{{ dbl_raise()*/
static double dbl_raise(x,y)
double x;
int y;
{
register int i=abs(y);
double val=1.0;
while (--i >= 0)
val *= x;
if (y < 0 ) return (1.0/val);
return(val);
}
/*}}}*/
/*{{{ timetic_fmt()*/
void
timetic_format(axis,amin,amax)
int axis;
double amin, amax;
{
double time_tic_just();
struct tm tmin, tmax;
*ticfmt[axis]=0; /* make sure we strcat to empty string */
ggmtime(&tmin,(double)time_tic_just(timelevel[axis],amin));
ggmtime(&tmax,(double)time_tic_just(timelevel[axis],amax));
/*
if ( tmax.tm_year == tmin.tm_year &&
tmax.tm_mon == tmin.tm_mon &&
tmax.tm_mday == tmin.tm_mday ) {
*/
if ( tmax.tm_year == tmin.tm_year &&
tmax.tm_yday == tmin.tm_yday ) {
/* same day, skip date */
if ( tmax.tm_hour != tmin.tm_hour ) {
strcpy(ticfmt[axis],"%H");
}
if ( timelevel[axis] < 3 ) {
if (strlen(ticfmt[axis]))
strcat(ticfmt[axis],":");
strcat(ticfmt[axis],"%M");
}
if ( timelevel[axis] < 2 ) {
strcat(ticfmt[axis],":%S");
}
} else {
if ( tmax.tm_year != tmin.tm_year ) {
/* different years, include year in ticlabel */
/* check convention, day/month or month/day */
if (strchr(timefmt,'m') < strchr(timefmt,'d')) {
strcpy(ticfmt[axis],"%m/%d/%");
} else {
strcpy(ticfmt[axis],"%d/%m/%");
}
if ( ((int)(tmax.tm_year/100)) != ((int)(tmin.tm_year/100)) ) {
strcat(ticfmt[axis],"Y");
} else {
strcat(ticfmt[axis],"y");
}
} else {
if (strchr(timefmt,'m') < strchr(timefmt,'d')) {
strcpy(ticfmt[axis],"%m/%d");
} else {
strcpy(ticfmt[axis],"%d/%m");
}
}
if ( timelevel[axis] < 4 ) {
strcat(ticfmt[axis],"\n%H:%M");
}
}
}
/*}}}*/
/*{{{ set_tic()*/
static double
set_tic(l10,guide)
double l10;
int guide;
{
double xnorm, tics, posns;
int fl=floor(l10);
xnorm = pow(10.0,l10-fl); /* approx number of decades */
posns = guide / xnorm; /* approx number of tic posns per decade */
if (posns > 10)
tics = 0.2; /* eg 0,0.2,0.4,... */
else if (posns > 4)
tics = 0.5; /* 0,0.5,1, */
else if (posns > 1)
tics = 1; /* 0,1,2,.... */
else if (posns > 0.5)
tics = 2; /* 0, 2, 4, 6 */
else
/* getting desperate... the ceil is to make sure we
* go over rather than under - eg plot [-10:10] x*x
* gives a range of about 99.999 - tics=xnorm gives
* tics at 0, 99.99 and 109.98 - BAD !
* This way, inaccuracy the other way will round
* up (eg 0->100.0001 => tics at 0 and 101
* I think latter is better than former
*/
tics = ceil(xnorm);
return (tics * dbl_raise(10.0,fl));
}
/*}}}*/
/*{{{ make_tics()*/
static double make_tics(axis, guide)
int axis,guide;
{
register double xr,tic,l10;
xr = fabs(min_array[axis]-max_array[axis]);
l10 = log10(xr);
tic = set_tic(l10, guide);
if (log_array[axis] && tic<1.0)
tic=1.0;
if (datatype[axis] == TIME) {
struct tm ftm, etm;
/* this is not fun */
ggmtime(&ftm,(double)min_array[axis]);
ggmtime(&etm,(double)max_array[axis]);
timelevel[axis] = 0; /* seconds */
if ( tic > 20 ) {
/* turn tic into units of minutes */
tic = set_tic(log10(xr/60.0),guide)*60;
timelevel[axis] = 1; /* minutes */
}
if ( tic > 20*60 ) {
/* turn tic into units of hours */
tic = set_tic(log10(xr/3600.0),guide)*3600;
timelevel[axis] = 2; /* hours */
}
if ( tic > 2*3600 ) {
/* need some tickling */
tic = set_tic(log10(xr/(3*3600.0)),guide)*3*3600;
}
if ( tic > 6*3600 ) {
/* turn tic into units of days */
tic = set_tic(log10(xr/DAY_SEC),guide)*DAY_SEC;
timelevel[axis] = 3; /* days */
}
if ( tic > 3*DAY_SEC ) {
/* turn tic into units of weeks */
tic = set_tic(log10(xr/WEEK_SEC),guide)*WEEK_SEC;
if ( tic < WEEK_SEC ) { /* force */
tic = WEEK_SEC;
}
timelevel[axis] = 4; /* weeks */
}
if ( tic > 3*WEEK_SEC ) {
/* turn tic into units of month */
tic = set_tic(log10(xr/MON_SEC),guide)*MON_SEC;
if ( tic < MON_SEC ) { /* force */
tic = MON_SEC;
}
timelevel[axis] = 5; /* month */
}
if ( tic > 2*MON_SEC ) {
/* turn tic into units of month */
tic = set_tic(log10(xr/(3*MON_SEC)),guide)*3*MON_SEC;
}
if ( tic > 6*MON_SEC ) {
/* turn tic into units of years */
tic = set_tic(log10(xr/YEAR_SEC),guide)*YEAR_SEC;
if ( tic < (YEAR_SEC/2) ) {
tic = YEAR_SEC / 2;
}
timelevel[axis] = 6; /* year */
}
}
return(tic);
}
/*}}}*/
void do_plot(plots, pcount)
struct curve_points *plots;
int pcount; /* count of plots in linked list */
{
/* BODGES BEFORE I FIX IT UP */
#define ytic ticstep[y_axis]
#define xtic ticstep[x_axis]
register struct termentry *t = term;
register int curve;
int axis_zero[AXIS_ARRAY_SIZE]; /* axes in terminal coords for FIRST_X_AXIS, etc */
register struct curve_points *this_plot = NULL;
register int xl=0, yl=0; /* avoid gcc -Wall warning */
register int key_count=0;
/* only a Pyramid would have this many registers! */
struct text_label *this_label;
struct arrow_def *this_arrow;
TBOOLEAN scaling;
char ss[MAX_LINE_LEN+1], *s, *e;
/* so that macros for x_min etc pick up correct values
* until this is done properly
*/
x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS;
/* Apply the desired viewport offsets. */
if (y_min < y_max) {
y_min -= boff;
y_max += toff;
} else {
y_max -= boff;
y_min += toff;
}
if (x_min < x_max) {
x_min -= loff;
x_max += roff;
} else {
x_max -= loff;
x_min += roff;
}
/* This used be x_max == x_min, but that caused an infinite loop once. */
if (fabs(x_max - x_min) < zero)
int_error("x_min should not equal x_max!",NO_CARET);
if (fabs(y_max - y_min) < zero)
int_error("y_min should not equal y_max!",NO_CARET);
/* INITIALIZE TERMINAL */
if (!term_init) {
(*t->init)();
term_init = TRUE;
}
/* compute boundary for plot (xleft, xright, ytop, ybot)
* also calculates tics, since xtics depend on xleft
* but xleft depends on ytics. Boundary calculations
* depend on term->v_char etc, so terminal must be
* initialised.
*/
scaling = (*t->scale)(xsize, ysize);
boundary(scaling,plots,pcount);
screen_ok = FALSE;
if (!term_graphics) {
(*t->graphics)();
term_graphics = TRUE;
} else if (multiplot && term_suspended) {
/* second plot of a multiplot, after interactive prompt */
if (t->resume)
(*t->resume)();
term_suspended = FALSE;
}
/* DRAW TICS AND GRID */
(*t->linetype)(-2); /* border linetype */
largest_polar_circle=0;
/* select first mapping */
x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS;
/* label first y axis tics */
if (ytics) {
int axis=map_x(ZERO);
/* set the globals ytick2d_callback() needs */
tic_just=RIGHT;
if (ytics&TICS_MIRROR)
tic_mirror=xright;
else
tic_mirror= -1; /* no thank you */
if ( (ytics&TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis, xleft, xright)) {
tic_start = axis;
tic_direction=-1;
/* put text at boundary if axis is close to boundary */
tic_text = ( ( (tic_start-xleft) > (3*t->h_char) ) ? tic_start : xleft) - t->h_char;
} else {
tic_start=xleft;
tic_direction=tic_in ? 1 : -1;
tic_text=xleft-t->h_char;
if (!tic_in) tic_text -= ticscale*(t->h_tic);
}
/* go for it */
gen_tics(FIRST_Y_AXIS, &yticdef,
grid&(GRID_Y|GRID_MY),
mytics, mytfreq, ytick2d_callback);
}
/* label first x axis tics */
if (xtics) {
int axis=map_y(ZERO);
/* set the globals xtick2d_callback() needs */
tic_just=JUST_TOP;
if (xtics&TICS_MIRROR)
tic_mirror=ytop;
else
tic_mirror= -1; /* no thank you */
if ( (xtics&TICS_ON_AXIS) && !log_array[FIRST_Y_AXIS] && inrange(axis, ybot, ytop)) {
tic_start=axis;
tic_direction=-1;
/* put text at boundary if axis is close to boundary */
if (tic_start - ybot > 2*t->v_char)
tic_text = tic_start - ticscale*t->v_tic - t->v_char;
else
tic_text = ybot - t->v_char;
} else {
tic_start=ybot;
tic_direction=tic_in ? 1 : -1;
tic_text=ybot-t->v_char;
if (!tic_in)
tic_text -= ticscale* t->v_tic;
}
/* go for it */
gen_tics(FIRST_X_AXIS, &xticdef,
grid&(GRID_X|GRID_MX),
mxtics, mxtfreq, xtick2d_callback);
}
/* select second mapping */
x_axis=SECOND_X_AXIS; y_axis=SECOND_Y_AXIS;
/* label second y axis tics */
if (y2tics) {
/* set the globalss ytick2d_callback() needs */
int axis=map_x(ZERO);
tic_just=LEFT;
if (y2tics&TICS_MIRROR)
tic_mirror=xleft;
else
tic_mirror= -1; /* no thank you */
if ((y2tics&TICS_ON_AXIS) && !log_array[FIRST_X_AXIS] && inrange(axis,xleft,xright)) {
tic_start = axis;
tic_direction=1;
/* put text at boundary if axis is close to boundary */
tic_text = ( ( (xright-tic_start) > (3*t->h_char) ) ? tic_start : xright) + t->h_char;
} else {
tic_start=xright;
tic_direction=tic_in ? -1 : 1;
tic_text=xright+t->h_char;
if (!tic_in)
tic_text += ticscale*(t->h_tic);
}
/* go for it */
gen_tics(SECOND_Y_AXIS, &y2ticdef,
grid&(GRID_Y2|GRID_MY2),
my2tics, my2tfreq, ytick2d_callback);
}
/* label second x axis tics */
if (x2tics) {
int axis=map_y(ZERO);
/* set the globals xtick2d_callback() needs */
tic_just=JUST_BOT;
if (x2tics&TICS_MIRROR)
tic_mirror=ybot;
else
tic_mirror= -1; /* no thank you */
if ( (x2tics&TICS_ON_AXIS) && !log_array[SECOND_Y_AXIS] && inrange(axis,ybot,ytop)) {
tic_start=axis;
tic_direction=1;
/* put text at boundary if axis is close to boundary */
tic_text = ( ( (ytop - tic_start ) > (2*t->v_char) ) ? tic_start : ytop) + t->v_char;
} else {
tic_start=ytop;
tic_direction=tic_in ? -1 : 1;
tic_text=ytop+t->v_char + x2ticdelta;
}
/* go for it */
gen_tics(SECOND_X_AXIS, &x2ticdef,
grid&(GRID_X2|GRID_MX2),
mx2tics, mx2tfreq, xtick2d_callback);
}
/* select first mapping */
x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS;
/* RADIAL LINES FOR POLAR GRID */
if (polar_grid_angle) {
double theta=0;
int ox=map_x(0);
int oy=map_y(0);
(*t->linetype)(grid_linetype);
for (theta=0; theta<6.29; theta += polar_grid_angle)
draw_clip_line(ox,oy,
map_x(largest_polar_circle*cos(theta)),
map_y(largest_polar_circle*sin(theta)));
}
/* DRAW AXES */
/* after grid so that axes linetypes are on top */
x_axis=FIRST_X_AXIS; y_axis=FIRST_Y_AXIS; /* chose scaling */
axis_zero[FIRST_X_AXIS] = map_y(0.0);
axis_zero[FIRST_Y_AXIS] = map_x(0.0);
if (axis_zero[FIRST_X_AXIS] < ybot || is_log_y)
axis_zero[FIRST_X_AXIS] = ybot; /* save for impulse plotting */
else if (axis_zero[FIRST_X_AXIS] >= ytop)
axis_zero[FIRST_X_AXIS] = ytop ;
else if (xzeroaxis > -3) {
(*t->linetype)(xzeroaxis);
(*t->move)(xleft,axis_zero[FIRST_X_AXIS]);
(*t->vector)(xright,axis_zero[FIRST_X_AXIS]);
}
if ((yzeroaxis>-3) && !is_log_x && axis_zero[FIRST_Y_AXIS] >= xleft && axis_zero[FIRST_Y_AXIS] < xright ) {
(*t->linetype)(yzeroaxis);
(*t->move)(axis_zero[FIRST_Y_AXIS],ybot);
(*t->vector)(axis_zero[FIRST_Y_AXIS],ytop);
}
x_axis=SECOND_X_AXIS; y_axis=SECOND_Y_AXIS; /* chose scaling */
axis_zero[SECOND_X_AXIS] = map_y(0.0);
axis_zero[SECOND_Y_AXIS] = map_x(0.0);
if (axis_zero[SECOND_X_AXIS] < ybot || is_log_y2)
axis_zero[SECOND_X_AXIS] = ybot; /* save for impulse plotting */
else if (axis_zero[SECOND_X_AXIS] >= ytop)
axis_zero[SECOND_X_AXIS] = ytop ;
else if (x2zeroaxis>-3) {
(*t->linetype)(x2zeroaxis);
(*t->move)(xleft,axis_zero[SECOND_X_AXIS]);
(*t->vector)(xright,axis_zero[SECOND_X_AXIS]);
}
if ((y2zeroaxis>-3) && !is_log_x2 && axis_zero[SECOND_Y_AXIS] >= xleft && axis_zero[SECOND_Y_AXIS] < xright ) {
(*t->linetype)(y2zeroaxis);
(*t->move)(axis_zero[SECOND_Y_AXIS],ybot);
(*t->vector)(axis_zero[SECOND_Y_AXIS],ytop);
}
/* DRAW PLOT BORDER */
(*t->linetype)(-2); /* border linetype */
if (draw_border) {
(*t->move)(xleft,ybot);
if(border_south){
(*t->vector)(xright,ybot);
} else {
(*t->move)(xright,ybot);
}
if(border_east){
(*t->vector)(xright,ytop);
} else {
(*t->move)(xright,ytop);
}
if(border_north){
(*t->vector)(xleft,ytop);
} else {
(*t->move)(xleft,ytop);
}
if(border_west){
(*t->vector)(xleft,ybot);
} else {
(*t->move)(xleft,ybot);
}
}
/* YLABEL */
if (*ylabel.text) {
strcpy(ss, ylabel.text);
if ((*t->text_angle)(1)) {
unsigned int x=(t->v_char)+xoffset*(t->xmax)+(ylabel.xoffset)*(t->v_char);
unsigned int y=(ytop+ybot)/2 + ylabel.yoffset*(t->h_char);
write_multiline(x,y,ss,CENTRE, JUST_TOP, 1, ylabel.font);
(*t->text_angle)(0);
} else {
/* really bottom just, but we know number of lines */
unsigned int x=(1+ylabel.xoffset)*(t->h_char) + xoffset*(t->xmax);
unsigned int y=ytop+(ylablin+ylabel.yoffset)*(t->v_char);
write_multiline(x,y,ss,LEFT,JUST_TOP, 0, ylabel.font);
}
}
/* Y2LABEL */
if (*y2label.text) {
strcpy(ss, y2label.text);
if ((*t->text_angle)(1)) {
/* we worked out posn in boundary() */
unsigned int x=y2label_x+(y2label.xoffset+1)*(t->v_char);
unsigned int y=(ytop+ybot)/2 + y2label.yoffset*(t->h_char);
write_multiline(x,y,ss,CENTRE, JUST_TOP, 1, y2label.font);
(*t->text_angle)(0);
} else {
/* really bottom just, but we know number of lines */
unsigned int x= y2label_x + (y2label.xoffset)*(t->h_char);
unsigned int y=ytop+(y2lablin+y2label.yoffset)*(t->v_char);
write_multiline(x,y,ss,RIGHT,JUST_TOP, 0, y2label.font);
}
}
/* XLABEL */
if (*xlabel.text) {
unsigned int x=(xright+xleft)/2 + xlabel.xoffset*(t->h_char);
unsigned int y=ybot - (t->v_char) + xlabel.yoffset*(t->v_char);
if (xtics&TICS_ON_BORDER) {
y -= xticlin*(t->v_char);
if (!tic_in)
y -= ticscale*(t->h_tic);
}
strcpy(ss, xlabel.text);
write_multiline(x,y,ss,CENTRE,JUST_TOP, 0, xlabel.font);
}
/* PLACE TITLE */
if (*title.text) {
unsigned int x = (xleft+xright)/2 + title.xoffset * t->h_char;
unsigned int y = ytop + (t->v_char)*top_margin + x2ticdelta + title.yoffset * t->v_char;
strcpy(ss, title.text);
write_multiline(x,y,ss,CENTRE, JUST_TOP, 0, title.font);
}
/* X2LABEL */
if (*x2label.text) {
unsigned int x=(xright+xleft)/2 + x2label.xoffset*(t->h_char);
unsigned int y= ytop + (t->v_char)*top_margin + x2ticdelta;
if (*title.text)
y -= (t->v_char)*(titlelin+0.5);
strcpy(ss, x2label.text);
write_multiline(x,y,ss,CENTRE,JUST_TOP, 0, x2label.font);
}
/* PLACE TIMEDATE */
if (*timelabel.text) {
char str[MAX_LINE_LEN+1];
time_t now;
unsigned int x = (timelabel.xoffset+1)*t->h_char;
unsigned int y = yoffset * t->ymax + (timelabel.yoffset+1)*t->v_char;
time(&now);
strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
if ((*t->text_angle)(1)) {
write_multiline(x,y,str,LEFT, JUST_TOP, 1, timelabel.font);
(*t->text_angle)(0);
} else {
write_multiline(x,y,str,LEFT, JUST_BOT, 0, timelabel.font);
}
}
/* PLACE LABELS */
for (this_label = first_label; this_label!=NULL;
this_label=this_label->next ) {
unsigned int x,y;
map_position(&this_label->place, &x, &y, "label");
strcpy(ss, this_label->text);
write_multiline(x, y, ss, this_label->pos, JUST_TOP, 0, this_label->font);
}
/* PLACE ARROWS */
for (this_arrow = first_arrow; this_arrow!=NULL;
this_arrow = this_arrow->next ) {
unsigned int sx,sy,ex,ey;
map_position(&this_arrow->start, &sx, &sy, "arrow");
map_position(&this_arrow->end, &ex, &ey, "arrow");
(*t->linetype)(this_arrow->line-1); /* arrow line type */
(*t->arrow)(sx, sy, ex, ey, this_arrow->head);
}
/* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
if (lkey) { /* may have been cancelled if something went wrong */
/* just use key_xl etc worked out in boundary() */
xl = key_xl + key_size_left;
yl = key_yt;
#if 0
}
if (lkey != 0 && strlen(key_title)) {
sprintf(ss,"%s\n",key_title);
s = ss;
yl -= t->v_char/2;
while( (e=(char *)strchr(s,'\n')) != NULL ) {
*e = '\0';
if ( key_just == JLEFT) {
(*t->justify_text)(LEFT);
(*t->put_text)(xl+key_text_left,yl,s);
} else {
if ((*t->justify_text)(RIGHT)) {
(*t->put_text)(xl+key_text_right,yl,s);
} else {
int x=xl+key_text_right-(t->h_char)*strlen(s);
if (key_hpos == TOUT || inrange(x, xleft, xright))
(*t->put_text)(x, yl,s);
}
}
s = ++e;
yl -= t->v_char;
}
yl += t->v_char/2;
}
yl_ref = yl -= key_entry_height/2; /* centralise the keys */
key_count = 0;
if (lkey && key_box>-3) {
(*t->linetype)(key_box);
(*t->move)(key_xl,key_yb);
(*t->vector)(key_xl,key_yt);
(*t->vector)(key_xr,key_yt);
(*t->vector)(key_xr,key_yb);
(*t->vector)(key_xl,key_yb);
/* draw a horizontal line between key title and first entry */ /* JFi */
(*t->move)(key_xl,key_yt - (ktitl_lines) * t->v_char); /* JFi */
(*t->vector)(key_xr,key_yt - (ktitl_lines) * t->v_char); /* JFi */
}
#else
if (*key_title) {
sprintf(ss,"%s\n",key_title);
s = ss;
yl -= t->v_char/2;
while( (e=(char *)strchr(s,'\n')) != NULL ) {
*e = '\0';
if ( key_just == JLEFT) {
(*t->justify_text)(LEFT);
(*t->put_text)(xl+key_text_left,yl,s);
} else {
if ((*t->justify_text)(RIGHT)) {
(*t->put_text)(xl+key_text_right,yl,s);
} else {
int x=xl+key_text_right-(t->h_char)*strlen(s);
if (key_hpos == TOUT || inrange(x, xleft, xright))
(*t->put_text)(x, yl,s);
}
}
s = ++e;
yl -= t->v_char;
}
yl += t->v_char/2;
}
yl_ref = yl -= key_entry_height/2; /* centralise the keys */
key_count = 0;
if (key_box>-3) {
(*t->linetype)(key_box);
(*t->move)(key_xl,key_yb);
(*t->vector)(key_xl,key_yt);
(*t->vector)(key_xr,key_yt);
(*t->vector)(key_xr,key_yb);
(*t->vector)(key_xl,key_yb);
/* draw a horizontal line between key title
and first entry */ /* JFi */
(*t->move) (key_xl,key_yt - (ktitl_lines) * t->v_char); /* JFi */
(*t->vector)(key_xr,key_yt - (ktitl_lines) * t->v_char); /* JFi */
}
} /* lkey */
#endif
/* DRAW CURVES */
this_plot = plots;
for (curve = 0; curve < pcount; this_plot = this_plot->next_cp, curve++) {
int localkey = lkey; /* a local copy */
/* set scaling for this plot's axes */
x_axis=this_plot->x_axis;
y_axis=this_plot->y_axis;
(*t->linetype)(this_plot->line_type);
if (this_plot->title && !*this_plot->title) {
localkey = 0;
} else {
(*t->linetype)(this_plot->line_type);
if (localkey != 0 && this_plot->title) {
key_count++;
if ( key_just == JLEFT) {
(*t->justify_text)(LEFT);
(*t->put_text)(xl+key_text_left,
yl,this_plot->title);
} else {
if ((*t->justify_text)(RIGHT)) {
(*t->put_text)(xl+key_text_right,
yl,this_plot->title);
} else {
int x=xl+key_text_right-(t->h_char)*strlen(this_plot->title);
if (key_hpos == TOUT ||
i_inrange(x, xleft, xright))
(*t->put_text)(x, yl,this_plot->title);
}
}
}
}
switch(this_plot->plot_style) {
case IMPULSES: {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_impulses(this_plot, axis_zero[y_axis], axis_zero[x_axis]);
break;
}
case LINES: {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_lines(this_plot);
break;
}
/* JG */ case STEPS: {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_steps(this_plot);
break;
}
/* JG */ case FSTEPS: {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_fsteps(this_plot);
break;
}
case POINTSTYLE: {
if (localkey != 0 && this_plot->title) {
(*t->point)(xl+key_point_offset,yl,
this_plot->point_type);
}
plot_points(this_plot);
break;
}
case LINESPOINTS: {
/* put lines */
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_lines(this_plot);
/* put points */
if (key != 0 && this_plot->title) {
(*t->point)(xl+key_point_offset,yl,
this_plot->point_type);
}
plot_points(this_plot);
break;
}
case DOTS: {
if (localkey != 0 && this_plot->title) {
(*t->point)(xl+key_point_offset,yl, -1);
}
plot_dots(this_plot);
break;
}
case YERRORBARS: {
if (localkey != 0 && this_plot->title) {
(*t->point)(xl+key_point_offset,yl,
this_plot->point_type);
}
plot_points(this_plot);
/* for functions, just like POINTSTYLE */
if (this_plot->plot_type == DATA) {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
if(bar_size>0.0){
(*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
(*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
}
}
plot_bars(this_plot);
}
break;
}
case XERRORBARS: {
if (localkey != 0 && this_plot->title) {
(*t->point)(xl+2*(t->h_char),yl,
this_plot->point_type);
}
plot_points(this_plot);
/* for functions, just like POINTSTYLE */
if (this_plot->plot_type == DATA) {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
if(bar_size > 0.0){
(*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
(*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
}
}
plot_bars(this_plot);
}
break;
}
case XYERRORBARS: {
if (localkey != 0 && this_plot->title) {
(*t->point)(xl+2*(t->h_char),yl,
this_plot->point_type);
}
plot_points(this_plot);
/* for functions, just like POINTSTYLE */
if (this_plot->plot_type == DATA) {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
if(bar_size > 0.0){
(*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
(*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
}
}
plot_bars(this_plot);
}
break;
}
case BOXXYERROR: {
if (localkey != 0 && this_plot->title) {
(*t->point)(xl+key_point_offset,yl,
this_plot->point_type);
}
plot_boxes(this_plot,axis_zero[x_axis]);
break;
}
case BOXERROR: {
if (this_plot->plot_type == DATA) {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
if(bar_size > 0.0){
(*t->move)(xl+key_sample_left,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_left,yl-ERRORBARTIC);
(*t->move)(xl+key_sample_right,yl+ERRORBARTIC);
(*t->vector)(xl+key_sample_right,yl-ERRORBARTIC);
}
}
plot_bars(this_plot);
}
}
/* no break */
case BOXES: {
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_boxes(this_plot,axis_zero[x_axis]);
break;
}
case VECTOR:
if (localkey != 0 && this_plot->title) {
(*t->move)(xl+key_sample_left,yl);
(*t->vector)(xl+key_sample_right,yl);
}
plot_vectors(this_plot);
break;
}
if (localkey && this_plot->title) {
if (key_count >= key_rows ) {
yl = yl_ref;
xl += key_col_wth;
key_count = 0;
} else
yl = yl - key_entry_height;
}
}
if (!multiplot){
(*t->text)();
term_graphics=FALSE;
}
(void) fflush(outfile);
}
/* BODGES */
#undef ytic
#undef xtic
/* plot_impulses:
* Plot the curves in IMPULSES style
*/
static void
plot_impulses(plot, yaxis_x, xaxis_y)
struct curve_points *plot;
int yaxis_x, xaxis_y;
{
int i;
int x,y;
struct termentry *t = term;
for (i = 0; i < plot->p_count; i++) {
switch (plot->points[i].type) {
case INRANGE: {
x = map_x(plot->points[i].x);
y = map_y(plot->points[i].y);
break;
}
case OUTRANGE: {
if (!inrange(plot->points[i].x, x_min,x_max))
continue;
x = map_x(plot->points[i].x);
if ((y_min < y_max
&& plot->points[i].y < y_min)
|| (y_max < y_min
&& plot->points[i].y > y_min))
y = map_y(y_min);
else
y = map_y(y_max);
break;
}
default: /* just a safety */
case UNDEFINED: {
continue;
}
}
if (polar)
(*t->move)(yaxis_x,xaxis_y);
else
(*t->move)(x,xaxis_y);
(*t->vector)(x,y);
}
}
/* plot_lines:
* Plot the curves in LINES style
*/
static void
plot_lines(plot)
struct curve_points *plot;
{
int i; /* point index */
int x,y; /* point in terminal coordinates */
struct termentry *t = term;
enum coord_type prev = UNDEFINED; /* type of previous point */
double ex, ey; /* an edge point */
double lx[2], ly[2]; /* two edge points */
for (i = 0; i < plot->p_count; i++) {
switch (plot->points[i].type) {
case INRANGE: {
x = map_x(plot->points[i].x);
y = map_y(plot->points[i].y);
if (prev == INRANGE) {
(*t->vector)(x,y);
} else if (prev == OUTRANGE) {
/* from outrange to inrange */
if (!clip_lines1) {
(*t->move)(x,y);
} else {
edge_intersect(plot->points, i, &ex, &ey);
(*t->move)(map_x(ex), map_y(ey));
(*t->vector)(x,y);
}
} else { /* prev == UNDEFINED */
(*t->move)(x,y);
(*t->vector)(x,y);
}
break;
}
case OUTRANGE: {
if (prev == INRANGE) {
/* from inrange to outrange */
if (clip_lines1) {
edge_intersect(plot->points, i, &ex, &ey);
(*t->vector)(map_x(ex), map_y(ey));
}
} else if (prev == OUTRANGE) {
/* from outrange to outrange */
if (clip_lines2) {
if (two_edge_intersect(plot->points, i, lx, ly)) {
(*t->move)(map_x(lx[0]), map_y(ly[0]));
(*t->vector)(map_x(lx[1]), map_y(ly[1]));
}
}
}
break;
}
default: /* just a safety */
case UNDEFINED: {
break;
}
}
prev = plot->points[i].type;
}
}
/* XXX - JG */
/* plot_steps:
* Plot the curves in STEPS style
*/
static void
plot_steps(plot)
struct curve_points *plot;
{
int i; /* point index */
int x,y; /* point in terminal coordinates */
struct termentry *t = term;
enum coord_type prev = UNDEFINED; /* type of previous point */
double ex, ey; /* an edge point */
double lx[2], ly[2]; /* two edge points */
int yprev=0; /* previous point coordinates */
for (i = 0; i < plot->p_count; i++) {
switch (plot->points[i].type) {
case INRANGE: {
x = map_x(plot->points[i].x);
y = map_y(plot->points[i].y);
if (prev == INRANGE) {
(*t->vector)(x,yprev);
(*t->vector)(x,y);
} else if (prev == OUTRANGE) {
/* from outrange to inrange */
if (!clip_lines1) {
(*t->move)(x,y);
} else { /* find edge intersection */
edge_intersect_steps(plot->points, i, &ex, &ey);
(*t->move)(map_x(ex), map_y(ey));
(*t->vector)(x,map_y(ey));
(*t->vector)(x,y);
}
} else { /* prev == UNDEFINED */
(*t->move)(x,y);
(*t->vector)(x,y);
}
yprev = y;
break;
}
case OUTRANGE: {
if (prev == INRANGE) {
/* from inrange to outrange */
if (clip_lines1) {
edge_intersect_steps(plot->points, i, &ex, &ey);
(*t->vector)(map_x(ex), yprev);
(*t->vector)(map_x(ex), map_y(ey));
}
} else if (prev == OUTRANGE) {
/* from outrange to outrange */
if (clip_lines2) {
if (two_edge_intersect_steps(plot->points, i, lx, ly)) {
(*t->move)(map_x(lx[0]), map_y(ly[0]));
(*t->vector)(map_x(lx[1]), map_y(ly[0]));
(*t->vector)(map_x(lx[1]), map_y(ly[1]));
}
}
}
break;
}
default: /* just a safety */
case UNDEFINED: {
break;
}
}
prev = plot->points[i].type;
}
}
/* XXX - HOE */
/* plot_fsteps:
* Plot the curves in STEPS style by step on forward yvalue
*/
static void
plot_fsteps(plot)
struct curve_points *plot;
{
int i; /* point index */
int x,y; /* point in terminal coordinates */
struct termentry *t = term;
enum coord_type prev = UNDEFINED; /* type of previous point */
double ex, ey; /* an edge point */
double lx[2], ly[2]; /* two edge points */
int xprev = 0; /* previous point coordinates */
for (i = 0; i < plot->p_count; i++) {
switch (plot->points[i].type) {
case INRANGE: {
x = map_x(plot->points[i].x);
y = map_y(plot->points[i].y);
if (prev == INRANGE) {
(*t->vector)(xprev,y);
(*t->vector)(x,y);
} else if (prev == OUTRANGE) {
/* from outrange to inrange */
if (!clip_lines1) {
(*t->move)(x,y);
} else { /* find edge intersection */
edge_intersect_fsteps(plot->points, i, &ex, &ey);
(*t->move)(map_x(ex), map_y(ey));
(*t->vector)(map_x(ex),y);
(*t->vector)(x,y);
}
} else { /* prev == UNDEFINED */
(*t->move)(x,y);
(*t->vector)(x,y);
}
xprev = x;
break;
}
case OUTRANGE: {
if (prev == INRANGE) {
/* from inrange to outrange */
if (clip_lines1) {
edge_intersect_fsteps(plot->points, i, &ex, &ey);
(*t->vector)(xprev,map_y(ey));
(*t->vector)(map_x(ex), map_y(ey));
}
} else if (prev == OUTRANGE) {
/* from outrange to outrange */
if (clip_lines2) {
if (two_edge_intersect_fsteps(plot->points, i, lx, ly)) {
(*t->move)(map_x(lx[0]), map_y(ly[0]));
(*t->vector)(map_x(lx[0]), map_y(ly[1]));
(*t->vector)(map_x(lx[1]), map_y(ly[1]));
}
}
}
break;
}
default: /* just a safety */
case UNDEFINED: {
break;
}
}
prev = plot->points[i].type;
}
}
/* plot_bars:
* Plot the curves in ERRORBARS style
* we just plot the bars; the points are plotted in plot_points
*/
static void
plot_bars(plot)
struct curve_points *plot;
{
int i; /* point index */
struct termentry *t = term;
double x, y; /* position of the bar */
double ylow, yhigh; /* the ends of the bars */
double xlow, xhigh;
unsigned int xM, ylowM, yhighM; /* the mapped version of above */
unsigned int yM, xlowM, xhighM;
TBOOLEAN low_inrange, high_inrange;
int tic = ERRORBARTIC;
/* Limitation: no boxes with x errorbars */
if ((plot->plot_style == YERRORBARS) || (plot->plot_style == XYERRORBARS) ||
(plot->plot_style == BOXERROR)){
/* Draw the vertical part of the bar */
for (i = 0; i < plot->p_count; i++) {
/* undefined points don't count */
if (plot->points[i].type == UNDEFINED)
continue;
/* check to see if in xrange */
x = plot->points[i].x;
if (! inrange(x, x_min, x_max))
continue;
xM = map_x(x);
/* find low and high points of bar, and check yrange */
yhigh = plot->points[i].yhigh;
ylow = plot->points[i].ylow;
high_inrange = inrange(yhigh, y_min,y_max);
low_inrange = inrange(ylow, y_min,y_max);
/* compute the plot position of yhigh */
if (high_inrange)
yhighM = map_y(yhigh);
else if (samesign(yhigh-y_max, y_max-y_min))
yhighM = map_y(y_max);
else
yhighM = map_y(y_min);
/* compute the plot position of ylow */
if (low_inrange)
ylowM = map_y(ylow);
else if (samesign(ylow-y_max, y_max-y_min))
ylowM = map_y(y_max);
else
ylowM = map_y(y_min);
if (!high_inrange && !low_inrange && ylowM == yhighM)
/* both out of range on the same side */
continue;
/* by here everything has been mapped */
(*t->move)(xM, ylowM);
(*t->vector)(xM, yhighM); /* draw the main bar */
if(bar_size > 0.0){
(*t->move)((unsigned int)(xM-bar_size*tic), ylowM); /* draw the bottom tic */
(*t->vector)((unsigned int)(xM+bar_size*tic), ylowM);
(*t->move)((unsigned int)(xM-bar_size*tic), yhighM); /* draw the top tic */
(*t->vector)((unsigned int)(xM+bar_size*tic), yhighM);
}
} /* for loop */
} /* if yerrorbars OR xyerrorbars */
if ((plot->plot_style == XERRORBARS) || (plot->plot_style == XYERRORBARS)){
/* Draw the horizontal part of the bar */
for (i = 0; i < plot->p_count; i++) {
/* undefined points don't count */
if (plot->points[i].type == UNDEFINED)
continue;
/* check to see if in yrange */
y = plot->points[i].y;
if (! inrange(y, y_min, y_max))
continue;
yM = map_y(y);
/* find low and high points of bar, and check xrange */
xhigh = plot->points[i].xhigh;
xlow = plot->points[i].xlow;
high_inrange = inrange(xhigh, x_min,x_max);
low_inrange = inrange(xlow, x_min,x_max);
/* compute the plot position of xhigh */
if (high_inrange)
xhighM = map_x(xhigh);
else if (samesign(xhigh-x_max, x_max-x_min))
xhighM = map_x(x_max);
else
xhighM = map_x(x_min);
/* compute the plot position of xlow */
if (low_inrange)
xlowM = map_x(xlow);
else if (samesign(xlow-x_max, x_max-x_min))
xlowM = map_x(x_max);
else
xlowM = map_x(x_min);
if (!high_inrange && !low_inrange && xlowM == xhighM)
/* both out of range on the same side */
continue;
/* by here everything has been mapped */
(*t->move)(xlowM, yM);
(*t->vector)(xhighM, yM); /* draw the main bar */
if(bar_size > 0.0){
(*t->move)(xlowM, (unsigned int)(yM-bar_size*tic)); /* draw the left tic */
(*t->vector)(xlowM, (unsigned int)(yM+bar_size*tic));
(*t->move)(xhighM, (unsigned int)(yM-bar_size*tic)); /* draw the right tic */
(*t->vector)(xhighM, (unsigned int)(yM+bar_size*tic));
}
} /* for loop */
} /* if xerrorbars OR xyerrorbars */
}
/* plot_boxes:
* Plot the curves in BOXES style
*/
static void
plot_boxes(plot,xaxis_y)
struct curve_points *plot;
int xaxis_y;
{
int i; /* point index */
int xl,xr,yt; /* point in terminal coordinates */
double dxl,dxr,dyt;
struct termentry *t = term;
enum coord_type prev = UNDEFINED; /* type of previous point */
TBOOLEAN boxxy = (plot->plot_style == BOXXYERROR);
for (i = 0; i < plot->p_count; i++) {
switch (plot->points[i].type) {
case OUTRANGE:
case INRANGE: {
if (plot->points[i].z<0.0) {
if (boxwidth<0.0) {
/* calculate width */
if (prev!=UNDEFINED)
dxl = (plot->points[i-1].x - plot->points[i].x)/2.0;
else
dxl = 0.0;
if (i < plot->p_count-1) {
if (plot->points[i+1].type!=UNDEFINED)
dxr = (plot->points[i+1].x - plot->points[i].x)/2.0;
else
dxr = -dxl;
}
else {
dxr = -dxl;
}
if (prev==UNDEFINED)
dxl = -dxr;
}
else {
dxr = boxwidth/2.0;
dxl = -dxr;
}
}
else {
dxr = plot->points[i].z/2.0;
dxl = -dxr;
}
if (boxxy) {
dxr = plot->points[i].xhigh;
dxl = plot->points[i].xlow;
dyt = plot->points[i].yhigh;
xaxis_y = map_y(plot->points[i].ylow);
}
else {
dxl= plot->points[i].x+dxl;
dxr= plot->points[i].x+dxr;
dyt= plot->points[i].y;
}
/* clip to border */
if ((y_min < y_max && dyt < y_min)
|| (y_max < y_min && dyt > y_min))
dyt = y_min;
if ((y_min < y_max && dyt > y_max)
|| (y_max<y_min && dyt < y_max))
dyt = y_max;
if ((x_min < x_max && dxr < x_min)
|| (x_max < x_min && dxr > x_min))
dxr = x_min;
if ((x_min < x_max && dxr > x_max)
|| (x_max<x_min && dxr < x_max))
dxr = x_max;
if ((x_min < x_max && dxl < x_min)
|| (x_max < x_min && dxl > x_min))
dxl = x_min;
if ((x_min < x_max && dxl > x_max)
|| (x_max<x_min && dxl < x_max))
dxl = x_max;
xl= map_x(dxl);
xr= map_x(dxr);
yt = map_y(dyt);
(*t->move)(xl,xaxis_y);
(*t->vector)(xl,yt);
(*t->vector)(xr,yt);
(*t->vector)(xr,xaxis_y);
(*t->vector)(xl,xaxis_y);
break;
}
default: /* just a safety */
case UNDEFINED: {
break;
}
}
prev = plot->points[i].type;
}
}
/* plot_points:
* Plot the curves in POINTSTYLE style
*/
static void
plot_points(plot)
struct curve_points *plot;
{
int i;
int x,y;
struct termentry *t = term;
for (i = 0; i < plot->p_count; i++) {
if (plot->points[i].type == INRANGE) {
x = map_x(plot->points[i].x);
y = map_y(plot->points[i].y);
/* do clipping if necessary */
if (!clip_points ||
( x >= xleft + p_width && y >= ybot + p_height
&& x <= xright - p_width && y <= ytop - p_height))
(*t->point)(x,y, plot->point_type);
}
}
}
/* plot_dots:
* Plot the curves in DOTS style
*/
static void
plot_dots(plot)
struct curve_points *plot;
{
int i;
int x,y;
struct termentry *t = term;
for (i = 0; i < plot->p_count; i++) {
if (plot->points[i].type == INRANGE) {
x = map_x(plot->points[i].x);
y = map_y(plot->points[i].y);
/* point type -1 is a dot */
(*t->point)(x,y, -1);
}
}
}
/* plot_vectors:
* Plot the curves in VECTORS style
*/
static void
plot_vectors(plot)
struct curve_points *plot;
{
int i;
int x1,y1,x2,y2;
struct termentry *t = term;
for (i = 0; i < plot->p_count; i++) {
if (plot->points[i].type == INRANGE) {
x1 = map_x(plot->points[i].xlow);
y1 = map_y(plot->points[i].ylow);
x2 = map_x(plot->points[i].xhigh);
y2 = map_y(plot->points[i].yhigh);
(*t->arrow)(x1,y1, x2,y2, TRUE);
}
}
}
/* single edge intersection algorithm */
/* Given two points, one inside and one outside the plot, return
* the point where an edge of the plot intersects the line segment defined
* by the two points.
*/
static void
edge_intersect(points, i, ex, ey)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *ex, *ey; /* the point where it crosses an edge */
{
/* global x_min, x_max, y_min, x_max */
double ix = points[i-1].x;
double iy = points[i-1].y;
double ox = points[i].x;
double oy = points[i].y;
double x, y; /* possible intersection point */
if(points[i].type == INRANGE)
{
/* swap points around so that ix/ix/iz are INRANGE and ox/oy/oz are OUTRANGE */
x = ix;ix = ox;ox = x;
y = iy;iy = oy;oy = y;
}
/* nasty degenerate cases, effectively drawing to an infinity point (?)
cope with them here, so don't process them as a "real" OUTRANGE point
If more than one coord is -VERYLARGE, then can't ratio the "infinities"
so drop out by returning the INRANGE point.
Obviously, only need to test the OUTRANGE point (coordinates) */
if(ox == -VERYLARGE || oy == -VERYLARGE)
{
*ex = ix;
*ey = iy;
if(ox == -VERYLARGE)
{
/* can't get a direction to draw line, so simply return INRANGE point */
if(oy == -VERYLARGE)
return;
*ex = x_min;
return;
}
/* obviously oy is -VERYLARGE and ox != -VERYLARGE */
*ey = y_min;
return;
}
/*
* Can't have case (ix == ox && iy == oy) as one point
* is INRANGE and one point is OUTRANGE.
*/
if (iy == oy) {
/* horizontal line */
/* assume inrange(iy, y_min, y_max) */
*ey = iy; /* == oy */
if (inrange(x_max, ix, ox))
*ex = x_max;
else if (inrange(x_min, ix, ox))
*ex = x_min;
else {
graph_error("error in edge_intersect");
}
return;
} else if (ix == ox) {
/* vertical line */
/* assume inrange(ix, x_min, x_max) */
*ex = ix; /* == ox */
if (inrange(y_max, iy, oy))
*ey = y_max;
else if (inrange(y_min, iy, oy))
*ey = y_min;
else {
graph_error("error in edge_intersect");
}
return;
}
/* slanted line of some kind */
/* does it intersect y_min edge */
if (inrange(y_min, iy, oy) && y_min != iy && y_min != oy) {
x = ix + (y_min-iy) * ((ox-ix) / (oy-iy));
if (inrange(x, x_min, x_max)) {
*ex = x;
*ey = y_min;
return; /* yes */
}
}
/* does it intersect y_max edge */
if (inrange(y_max, iy, oy) && y_max != iy && y_max != oy) {
x = ix + (y_max-iy) * ((ox-ix) / (oy-iy));
if (inrange(x, x_min, x_max)) {
*ex = x;
*ey = y_max;
return; /* yes */
}
}
/* does it intersect x_min edge */
if (inrange(x_min, ix, ox) && x_min != ix && x_min != ox) {
y = iy + (x_min-ix) * ((oy-iy) / (ox-ix));
if (inrange(y, y_min, y_max)) {
*ex = x_min;
*ey = y;
return;
}
}
/* does it intersect x_max edge */
if (inrange(x_max, ix, ox) && x_max != ix && x_max != ox) {
y = iy + (x_max-ix) * ((oy-iy) / (ox-ix));
if (inrange(y, y_min, y_max)) {
*ex = x_max;
*ey = y;
return;
}
}
/* If we reach here, the inrange point is on the edge, and
* the line segment from the outrange point does not cross any
* other edges to get there. In this case, we return the inrange
* point as the 'edge' intersection point. This will basically draw
* line.
*/
*ex = ix;
*ey = iy;
return;
}
/* XXX - JG */
/* single edge intersection algorithm for "steps" curves */
/*
* Given two points, one inside and one outside the plot, return
* the point where an edge of the plot intersects the line segments
* forming the step between the two points.
*
* Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
* P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
* (x2,y1)->(x2,y2).
*/
static void
edge_intersect_steps(points, i, ex, ey)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *ex, *ey; /* the point where it crosses an edge */
{
/* global x_min, x_max, y_min, x_max */
double ax = points[i-1].x;
double ay = points[i-1].y;
double bx = points[i].x;
double by = points[i].y;
if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
if (inrange(ay,y_min,y_max)) {
*ey = ay;
if (ax > x_max)
*ex = x_max;
else /* x < x_min */
*ex = x_min;
} else {
*ex = bx;
if (ay > y_max)
*ey = y_max;
else /* y < y_min */
*ey = y_min;
}
} else { /* from INRANGE to OUTRANGE */
if (inrange(bx,x_min,x_max)) {
*ex = bx;
if (by > y_max)
*ey = y_max;
else /* y < y_min */
*ey = y_min;
} else {
*ey = ay;
if (bx > x_max)
*ex = x_max;
else /* x < x_min */
*ex = x_min;
}
}
return;
}
/* XXX - HOE */
/* single edge intersection algorithm for "fsteps" curves */
/* fsteps means step on forward y-value.
* Given two points, one inside and one outside the plot, return
* the point where an edge of the plot intersects the line segments
* forming the step between the two points.
*
* Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
* P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
* (x1,y2)->(x2,y2).
*/
static void
edge_intersect_fsteps(points, i, ex, ey)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *ex, *ey; /* the point where it crosses an edge */
{
/* global x_min, x_max, y_min, x_max */
double ax = points[i-1].x;
double ay = points[i-1].y;
double bx = points[i].x;
double by = points[i].y;
if (points[i].type == INRANGE) { /* from OUTRANGE to INRANG */
if (inrange(ax,x_min,x_max)) {
*ex = ax;
if (ay > y_max)
*ey = y_max;
else /* y < y_min */
*ey = y_min;
} else {
*ey = by;
if (bx > x_max)
*ex = x_max;
else /* x < x_min */
*ex = x_min;
}
} else { /* from INRANGE to OUTRANGE */
if (inrange(by,y_min,y_max)) {
*ey = by;
if (bx > x_max)
*ex = x_max;
else /* x < x_min */
*ex = x_min;
} else {
*ex = ax;
if (by > y_max)
*ey = y_max;
else /* y < y_min */
*ey = y_min;
}
}
return;
}
/* XXX - JG */
/* double edge intersection algorithm for "steps" plot */
/* Given two points, both outside the plot, return the points where an
* edge of the plot intersects the line segments forming a step
* by the two points. There may be zero, one, two, or an infinite number
* of intersection points. (One means an intersection at a corner, infinite
* means overlaying the edge itself). We return FALSE when there is nothing
* to draw (zero intersections), and TRUE when there is something to
* draw (the one-point case is a degenerate of the two-point case and we do
* not distinguish it - we draw it anyway).
*
* Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
* P1 to P2 is drawn as two line segments: (x1,y1)->(x2,y1) and
* (x2,y1)->(x2,y2).
*/
static TBOOLEAN /* any intersection? */
two_edge_intersect_steps(points, i, lx, ly)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
{
/* global x_min, x_max, y_min, x_max */
double ax = points[i-1].x;
double ay = points[i-1].y;
double bx = points[i].x;
double by = points[i].y;
if ( max(ax,bx) < x_min || min(ax,bx) > x_max ||
max(ay,by) < y_min || min(ay,by) > y_max ||
( (ay > y_max || ay < y_min) &&
(bx > x_max || bx < x_min) ) ) {
return(FALSE);
} else if (inrange(ay,y_min,y_max) && inrange(bx,x_min,x_max)) { /* corner of step inside plotspace */
*ly++ = ay;
if (ax < x_min)
*lx++ = x_min;
else
*lx++ = x_max;
*lx++ = bx;
if (by < y_min)
*ly++ = y_min;
else
*ly++ = y_max;
return(TRUE);
} else if (inrange(ay,y_min,y_max)) { /* cross plotspace in x-direction */
*lx++ = x_min;
*ly++ = ay;
*lx++ = x_max;
*ly++ = ay;
return(TRUE);
} else if (inrange(ax,x_min,x_max)) { /* cross plotspace in y-direction */
*lx++ = bx;
*ly++ = y_min;
*lx++ = bx;
*ly++ = y_max;
return(TRUE);
} else
return(FALSE);
}
/* XXX - HOE */
/* double edge intersection algorithm for "fsteps" plot */
/* Given two points, both outside the plot, return the points where an
* edge of the plot intersects the line segments forming a step
* by the two points. There may be zero, one, two, or an infinite number
* of intersection points. (One means an intersection at a corner, infinite
* means overlaying the edge itself). We return FALSE when there is nothing
* to draw (zero intersections), and TRUE when there is something to
* draw (the one-point case is a degenerate of the two-point case and we do
* not distinguish it - we draw it anyway).
*
* Recall that if P1 = (x1,y1) and P2 = (x2,y2), the step from
* P1 to P2 is drawn as two line segments: (x1,y1)->(x1,y2) and
* (x1,y2)->(x2,y2).
*/
static TBOOLEAN /* any intersection? */
two_edge_intersect_fsteps(points, i, lx, ly)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
{
/* global x_min, x_max, y_min, x_max */
double ax = points[i-1].x;
double ay = points[i-1].y;
double bx = points[i].x;
double by = points[i].y;
if ( max(ax,bx) < x_min || min(ax,bx) > x_max ||
max(ay,by) < y_min || min(ay,by) > y_max ||
( (by > y_max || by < y_min) &&
(ax > x_max || ax < x_min) ) ) {
return(FALSE);
} else if (inrange(by,y_min,y_max) && inrange(ax,x_min,x_max)) { /* corner of step inside plotspace */
*lx++ = ax;
if (ay < y_min)
*ly++ = y_min;
else
*ly++ = y_max;
*ly = by;
if (bx < x_min)
*lx = x_min;
else
*lx = x_max;
return(TRUE);
} else if (inrange(by,y_min,y_max)) { /* cross plotspace in x-direction */
*lx++ = x_min;
*ly++ = by;
*lx = x_max;
*ly = by;
return(TRUE);
} else if (inrange(ax,x_min,x_max)) { /* cross plotspace in y-direction */
*lx++ = ax;
*ly++ = y_min;
*lx = ax;
*ly = y_max;
return(TRUE);
} else
return(FALSE);
}
/* double edge intersection algorithm */
/* Given two points, both outside the plot, return
* the points where an edge of the plot intersects the line segment defined
* by the two points. There may be zero, one, two, or an infinite number
* of intersection points. (One means an intersection at a corner, infinite
* means overlaying the edge itself). We return FALSE when there is nothing
* to draw (zero intersections), and TRUE when there is something to
* draw (the one-point case is a degenerate of the two-point case and we do
* not distinguish it - we draw it anyway).
*/
static TBOOLEAN /* any intersection? */
two_edge_intersect(points, i, lx, ly)
struct coordinate GPHUGE *points; /* the points array */
int i; /* line segment from point i-1 to point i */
double *lx, *ly; /* lx[2], ly[2]: points where it crosses edges */
{
/* global x_min, x_max, y_min, x_max */
int count;
double ix = points[i-1].x;
double iy = points[i-1].y;
double ox = points[i].x;
double oy = points[i].y;
double t[4];
double swap;
double t_min, t_max;
#if 0
fprintf(stderr, "\ntwo_edge_intersect (%g, %g) and (%g, %g) : ",
points[i-1].x, points[i-1].y,
points[i].x, points[i].y);
#endif
/* nasty degenerate cases, effectively drawing to an infinity point (?)
cope with them here, so don't process them as a "real" OUTRANGE point
If more than one coord is -VERYLARGE, then can't ratio the "infinities"
so drop out by returning FALSE */
count = 0;
if(ix == -VERYLARGE) count++;
if(ox == -VERYLARGE) count++;
if(iy == -VERYLARGE) count++;
if(oy == -VERYLARGE) count++;
/* either doesn't pass through graph area *or*
can't ratio infinities to get a direction to draw line, so simply return(FALSE) */
if(count > 1){
#if 0
fprintf(stderr, "\tA\n");
#endif
return(FALSE);
}
if(ox == -VERYLARGE || ix == -VERYLARGE)
{
if(ix == -VERYLARGE)
{
/* swap points so ix/iy don't have a -VERYLARGE component */
swap = ix;ix = ox;ox = swap;
swap = iy;iy = oy;oy = swap;
}
/* check actually passes through the graph area */
if(ix > x_max && inrange(iy, y_min, y_max))
{
lx[0] = x_min;
ly[0] = iy;
lx[1] = x_max;
ly[1] = iy;
#if 0
fprintf(stderr, "(%g %g) -> (%g %g)",
lx[0], ly[0], lx[1], ly[1]);
#endif
return(TRUE);
}
else {
#if 0
fprintf(stderr, "\tB\n");
#endif
return(FALSE);
}
}
if(oy == -VERYLARGE || iy == -VERYLARGE)
{
if(iy == -VERYLARGE)
{
/* swap points so ix/iy don't have a -VERYLARGE component */
swap = ix;ix = ox;ox = swap;
swap = iy;iy = oy;oy = swap;
}
/* check actually passes through the graph area */
if(iy > y_max && inrange(ix, x_min, x_max))
{
lx[0] = ix;
ly[0] = y_min;
lx[1] = ix;
ly[1] = y_max;
#if 0
fprintf(stderr, "(%g %g) -> (%g %g)",
lx[0], ly[0], lx[1], ly[1]);
#endif
return(TRUE);
}
else {
#if 0
fprintf(stderr, "\tC\n");
#endif
return(FALSE);
}
}
/*
* Special horizontal/vertical, etc. cases are checked and remaining
* slant lines are checked separately.
*
* The slant line intersections are solved using the parametric form
* of the equation for a line, since if we test x/y min/max planes explicitly
* then e.g. a line passing through a corner point (x_min,y_min)
* actually intersects 2 planes and hence further tests would be required
* to anticipate this and similar situations.
*/
/*
* Can have case (ix == ox && iy == oy) as both points OUTRANGE
*/
if(ix == ox && iy == oy)
{
/* but as only define single outrange point, can't intersect graph area */
return(FALSE);
}
if(ix == ox) {
/* line parallel to y axis */
/* x coord must be in range, and line must span both y_min and y_max */
/* note that spanning y_min implies spanning y_max, as both points OUTRANGE */
if(!inrange(ix, x_min, x_max))
{
return(FALSE);
}
if (inrange(y_min, iy, oy)) {
lx[0] = ix;
ly[0] = y_min;
lx[1] = ix;
ly[1] = y_max;
#if 0
fprintf(stderr, "(%g %g) -> (%g %g)",
lx[0], ly[0], lx[1], ly[1]);
#endif
return(TRUE);
} else
return(FALSE);
}
if(iy == oy) {
/* already checked case (ix == ox && iy == oy) */
/* line parallel to x axis */
/* y coord must be in range, and line must span both x_min and x_max */
/* note that spanning x_min implies spanning x_max, as both points OUTRANGE */
if(!inrange(iy, y_min, y_max))
{
return(FALSE);
}
if (inrange(x_min, ix, ox)) {
lx[0] = x_min;
ly[0] = iy;
lx[1] = x_max;
ly[1] = iy;
#if 0
fprintf(stderr, "(%g %g) -> (%g %g)",
lx[0], ly[0], lx[1], ly[1]);
#endif
return(TRUE);
} else
return(FALSE);
}
/* nasty 2D slanted line in an xy plane */
/*
Solve parametric equation
(ix, iy) + t (diff_x, diff_y)
where 0.0 <= t <= 1.0 and
diff_x = (ox - ix);
diff_y = (oy - iy);
*/
t[0] = (x_min - ix)/(ox - ix);
t[1] = (x_max - ix)/(ox - ix);
if(t[0] > t[1]) {
swap = t[0];t[0] = t[1];t[1] = swap;
}
t[2] = (y_min - iy)/(oy - iy);
t[3] = (y_max - iy)/(oy - iy);
if(t[2] > t[3]) {
swap = t[2];t[2] = t[3];t[3] = swap;
}
t_min = max(max(t[0],t[2]),0.0);
t_max = min(min(t[1],t[3]),1.0);
if(t_min > t_max)
return(FALSE);
lx[0] = ix + t_min * (ox - ix);
ly[0] = iy + t_min * (oy - iy);
lx[1] = ix + t_max * (ox - ix);
ly[1] = iy + t_max * (oy - iy);
/*
* Can only have 0 or 2 intersection points -- only need test one coord
*/
if(inrange(lx[0], x_min, x_max) &&
inrange(ly[0], y_min, y_max))
{
#if 0
fprintf(stderr, "(%g %g) -> (%g %g)",
lx[0], ly[0], lx[1], ly[1]);
#endif
return(TRUE);
}
return(FALSE);
}
/* justify ticplace to a proper date-time value */
double
time_tic_just(level,ticplace)
int level;
double ticplace;
{
struct tm tm;
if (level <= 0) {
return(ticplace);
}
ggmtime(&tm,(double)ticplace);
if (level > 0) { /* units of minutes */
if ( tm.tm_sec > 50 )
tm.tm_min ++;
tm.tm_sec = 0;
}
if (level > 1) { /* units of hours */
if ( tm.tm_min > 50 )
tm.tm_hour ++;
tm.tm_min = 0;
}
if (level > 2) { /* units of days */
if ( tm.tm_hour > 14 ) {
tm.tm_hour = 0;
tm.tm_mday = 0;
tm.tm_yday ++;
ggmtime(&tm,(double)gtimegm(&tm));
} else if (tm.tm_hour > 7 ) {
tm.tm_hour = 12;
} else if (tm.tm_hour > 3 ) {
tm.tm_hour = 6;
} else {
tm.tm_hour = 0;
}
}
/* skip it, I have not bothered with weekday so far */
if (level > 4) { /* units of month */
if ( tm.tm_mday > 25 ) {
tm.tm_mon ++;
if (tm.tm_mon > 11) {
tm.tm_year++;
tm.tm_mon = 0;
}
}
tm.tm_mday = 1;
}
if (level > 5) {
if ( tm.tm_mon >= 7 )
tm.tm_year ++;
tm.tm_mon = 0;
}
ticplace = (double) gtimegm(&tm);
ggmtime(&tm,(double)gtimegm(&tm));
return(ticplace);
}
/* make smalltics for time-axis */
double
make_ltic(tlevel,incr)
int tlevel;
double incr;
{
double tinc;
tinc = 0;
if ( tlevel < 0 ) tlevel = 0;
switch ( tlevel ) {
case 0:
case 1: {
if ( incr >= 20 )
tinc = 10;
if ( incr >= 60 )
tinc = 30;
if ( incr >= 2*60 )
tinc = 60;
if ( incr >= 5*60 )
tinc = 2*60;
if ( incr >= 10*60 )
tinc = 5*60;
if ( incr >= 20*60 )
tinc = 10*60;
break;
}
case 2: {
if ( incr >= 20*60 )
tinc = 10*60;
if ( incr >= 3600 )
tinc = 30*60;
if ( incr >= 2*3600 )
tinc = 3600;
if ( incr >= 5*3600 )
tinc = 2*3600;
if ( incr >= 10*3600 )
tinc = 5*3600;
if ( incr >= 20*3600 )
tinc = 10*3600;
break;
}
case 3: {
if ( incr > 2*3600 )
tinc = 3600;
if ( incr > 4*3600 )
tinc = 2*3600;
if ( incr > 7*3600 )
tinc = 3*3600;
if ( incr > 13*3600 )
tinc = 6*3600;
if ( incr > DAY_SEC )
tinc = 12*3600;
if ( incr > 2*DAY_SEC )
tinc = DAY_SEC;
break;
}
case 4: { /* week: tic per day */
if ( incr > 2*DAY_SEC )
tinc = DAY_SEC;
if ( incr > 7*DAY_SEC )
tinc = 7*DAY_SEC;
break;
}
case 5: { /* month */
if ( incr > 2*DAY_SEC )
tinc = DAY_SEC;
if ( incr > 15*DAY_SEC )
tinc = 10*DAY_SEC;
if ( incr > 2*MON_SEC )
tinc = MON_SEC;
if ( incr > 6*MON_SEC )
tinc = 3*MON_SEC;
if ( incr > 2*YEAR_SEC )
tinc = YEAR_SEC;
break;
}
case 6: { /* year */
if ( incr > 2*MON_SEC )
tinc = MON_SEC;
if ( incr > 6*MON_SEC )
tinc = 3*MON_SEC;
if ( incr > 2*YEAR_SEC )
tinc = YEAR_SEC;
if ( incr > 10*YEAR_SEC )
tinc = 5*YEAR_SEC;
if ( incr > 50*YEAR_SEC )
tinc = 10*YEAR_SEC;
if ( incr > 100*YEAR_SEC )
tinc = 20*YEAR_SEC;
if ( incr > 200*YEAR_SEC )
tinc = 50*YEAR_SEC;
if ( incr > 300*YEAR_SEC )
tinc = 100*YEAR_SEC;
break;
}
}
return(tinc);
}
void write_multiline(x,y,text,hor,vert,angle, font)
unsigned int x,y;
char *text;
int hor,vert; /* horizontal and vertical just - text in hor direction despite angle */
int angle; /* assume term has already been set for this */
char *font; /* NULL or "" means use default */
{
/* assumes we are free to mangle the text */
register struct termentry *t = term;
char *p;
if (vert != JUST_TOP) {
/* count lines and adjust y */
int lines=0; /* number of linefeeds - one fewer than lines */
for (p=text; *p; ++p)
if (*p=='\n') ++lines;
if (angle)
x -= (vert*lines*t->v_char)/2;
else
y += (vert*lines*t->v_char)/2;
}
if (font && *font)
(*t->set_font)(font);
for(;;) { /* we will explicitly break out */
if ( (p=strchr(text, '\n')) != NULL )
*p=0; /* terminate the string */
if ((*t->justify_text)(hor)) {
(*t->put_text)(x, y, text);
} else {
int fix=hor*(t->h_char)*strlen(text)/2;
if (angle)
(*t->put_text)(x, y-fix, text);
else
(*t->put_text)(x - fix, y, text);
}
if (angle)
x += t->v_char;
else
y -= t->v_char;
if (!p)
break;
text = p+1;
} /* unconditional branch back to the for(;;) - just a goto ! */
if (font && *font)
(*t->set_font)(default_font);
}
/* display a x-axis ticmark - called by gen_ticks */
/* also uses global tic_start, tic_direction, tic_text and tic_just */
static void xtick2d_callback(axis, place, text, grid)
int axis;
double place;
char *text;
int grid; /* linetype or -2 for no grid */
{
register struct termentry *t = term;
/* minitick if text is NULL - beware - h_tic is unsigned */
int ticsize = tic_direction*(int)(t->v_tic)*(text ? ticscale : miniticscale);
unsigned int x=map_x(place);
if (grid > -2) {
(*t->linetype)(grid);
if (polar_grid_angle) {
double x=place, y=0, s=sin(0.1), c=cos(0.1);
int i;
if (place > largest_polar_circle) largest_polar_circle=place;
else if (-place > largest_polar_circle) largest_polar_circle=-place;
clip_move(map_x(x),map_y(0));
for (i=1;i<=63 /* 2pi/0.1*/; ++i) {
{
/* cos(t+dt)=cos(t)cos(dt)-sin(t)cos(dt) */
double tx=x*c-y*s;
/* sin(t+dt)=sin(t)cos(dt)+cos(t)sin(dt) */
y=y*c+x*s;
x=tx;
}
clip_vector(map_x(x), map_y(y));
}
} else {
if (x < key_xr && x > key_xl ) {
if ( key_yb > ybot ) {
(*t->move)(x, ybot);
(*t->vector)(x, key_yb);
}
if ( key_yt < ytop ) {
(*t->move)(x,key_yt);
(*t->vector)(x, ytop);
}
} else {
(*t->move)(x, ybot);
(*t->vector)(x, ytop);
}
}
(*t->linetype)(-2); /* border linetype */
}
/* we precomputed tic posn and text posn in global vars */
(*t->move)(x, tic_start);
(*t->vector)(x,tic_start+ticsize);
if (tic_mirror >= 0) {
(*t->move)(x, tic_mirror);
(*t->vector)(x,tic_mirror-ticsize);
}
if (text) {
write_multiline(x, tic_text, text, CENTRE, tic_just, 0, NULL);
}
}
/* display a y-axis ticmark - called by gen_ticks */
/* also uses global tic_start, tic_direction, tic_text and tic_just */
static void ytick2d_callback(axis, place, text, grid)
int axis;
double place;
char *text;
int grid; /* linetype or -2 */
{
register struct termentry *t = term;
/* minitick if text is NULL - v_tic is unsigned */
int ticsize = tic_direction*(int)(t->h_tic)*(text ? ticscale : miniticscale);
unsigned int y=map_y(place);
if (grid > -2) {
(*t->linetype)(grid);
if (polar_grid_angle) {
double x=0, y=place, s=sin(0.1), c=cos(0.1);
int i;
if (place > largest_polar_circle) largest_polar_circle=place;
else if (-place > largest_polar_circle) largest_polar_circle=-place;
clip_move(map_x(x),map_y(y));
for (i=1;i<=63 /* 2pi/0.1*/; ++i) {
{
/* cos(t+dt)=cos(t)cos(dt)-sin(t)cos(dt) */
double tx=x*c-y*s;
/* sin(t+dt)=sin(t)cos(dt)+cos(t)sin(dt) */
y=y*c+x*s;
x=tx;
}
clip_vector( map_x(x), map_y(y) );
}
} else {
if (y < key_yt && y > key_yb && key_xl < xright /* catch TOUT */ ) {
if ( key_xl > xleft ) {
(*t->move)(xleft, y);
(*t->vector)(key_xl, y);
}
if ( key_xr < xright ) {
(*t->move)(key_xr, y);
(*t->vector)(xright, y);
}
} else {
(*t->move)(xleft, y);
(*t->vector)(xright, y);
}
}
(*t->linetype)(-2); /* border linetype */
}
/* we precomputed tic posn and text posn */
(*t->move)(tic_start, y);
(*t->vector)(tic_start+ticsize, y);
if (tic_mirror >= 0) {
(*t->move)(tic_mirror, y);
(*t->vector)(tic_mirror-ticsize, y);
}
if (text) {
write_multiline(tic_text, y, text, tic_just, JUST_CENTRE, 0, NULL);
}
}
int
label_width(str,lines)
char *str;
int *lines;
{
char lab[MAX_LINE_LEN+1], *s, *e;
int mlen, len, l;
l = mlen = len = 0;
sprintf(lab,"%s\n",str);
s = lab;
while( (e=(char *)strchr(s,'\n')) ) {
*e = '\0';
len = strlen(s); /* = e-s ? */
if ( len > mlen ) mlen = len;
if ( len || l ) l++;
s = ++e;
}
/* lines=NULL => not interested - div */
if (lines) *lines = l;
return(mlen);
}
void setup_tics(axis,ticdef, format, max)
int axis;
struct ticdef *ticdef;
char *format;
int max; /* approx max number of slots available */
{
double tic;
int fixmin=(auto_array[axis] & 1) != 0;
int fixmax=(auto_array[axis] & 2) != 0;
if (ticdef->type == TIC_SERIES) {
ticstep[axis] = tic = ticdef->def.series.incr;
fixmin &= (ticdef->def.series.start == -VERYLARGE);
fixmax &= (ticdef->def.series.end == VERYLARGE);
} else if (ticdef->type == TIC_COMPUTED) {
ticstep[axis] = tic = make_tics(axis, max);
}
if (fixmin) {
if (min_array[axis] < max_array[axis])
min_array[axis] = tic * floor(min_array[axis]/tic);
else
min_array[axis] = tic * ceil(min_array[axis]/tic);
}
if (fixmax) {
if (min_array[axis] < max_array[axis])
max_array[axis] = tic * ceil(max_array[axis]/tic);
else
max_array[axis] = tic * floor(max_array[axis]/tic);
}
if (datatype[axis] == TIME && format_is_numeric[axis])
/* invent one for them */
timetic_format(axis, min_array[axis],max_array[axis]);
else
strcpy(ticfmt[axis], format);
}
/*{{{ mant_exp - split into mantissa and/or exponent*/
static void mant_exp(log_base, x, scientific, m,p)
double log_base, x;
int scientific; /* round to power of 3 */
double *m;
int *p; /* results */
{
int sign=1;
double l10;
int power;
/*{{{ check 0*/
if (x==0) {
if (m) *m=0;
if (p) *p=0;
return;
}
/*}}}*/
/*{{{ check -ve*/
if (x<0) {
sign=(-1);
x=(-x);
}
/*}}}*/
l10=log10(x) / log_base;
power = floor(l10);
if (scientific) {
power = 3*(power/3);
}
if (m) *m = sign * pow( 10.0, (l10-power)*log_base );
if (p) *p = power;
}
/*}}}*/
/*{{{ gprintf*/
/* extended sprintf */
void gprintf(dest, format, log_base, x)
char *dest, *format;
double log_base, x; /* we print one number in a number of different formats */
{
char temp[MAX_LINE_LEN];
char *t;
for (;;) {
/*{{{ copy to dest until %*/
while (*format != '%')
if (!(*dest++ = *format++))
return; /* end of format */
/*}}}*/
/*{{{ check for %%*/
if (format[1]=='%') {
*dest++ = '%';
format += 2;
continue;
}
/*}}}*/
/*{{{ copy format part to temp, excluding conversion character*/
t=temp;
*t++ = '%';
while (isdigit(*++format) || *format=='.' || *format=='-' || *format=='+')
*t++ = *format;
/*}}}*/
/*{{{ convert conversion character*/
switch (*format) {
/*{{{ x and o*/
case 'x': case 'X':
case 'o': case 'O':
t[0]=*format++;
t[1]=0;
sprintf(dest, temp, (int) x);
dest += strlen(dest);
break;
/*}}}*/
/*{{{ e, f and g*/
case 'e': case 'E':
case 'f': case 'F':
case 'g': case 'G':
t[0]=*format++;
t[1]=0;
sprintf(dest, temp, x);
dest += strlen(dest);
break;
/*}}}*/
/*{{{ l*/
case 'l':
{
double mantissa;
mant_exp(log_base, x, 0, &mantissa, NULL);
t[0]='f';
t[1]=0;
sprintf(dest, temp, mantissa);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
/*{{{ t*/
case 't':
{
double mantissa;
mant_exp(1.0, x, 0, &mantissa, NULL);
t[0]='f';
t[1]=0;
sprintf(dest, temp, mantissa);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
/*{{{ s*/
case 's':
{
double mantissa;
mant_exp(1.0, x, 1, &mantissa, NULL);
t[0]='f';
t[1]=0;
sprintf(dest, temp, mantissa);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
/*{{{ L*/
case 'L':
{
int power;
mant_exp(log_base, x, 0, NULL, &power);
t[0]='d';
t[1]=0;
sprintf(dest, temp, power);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
/*{{{ T*/
case 'T':
{
int power;
mant_exp(1.0, x, 0, NULL, &power);
t[0]='d';
t[1]=0;
sprintf(dest, temp, power);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
/*{{{ S*/
case 'S':
{
int power;
mant_exp(1.0, x, 1, NULL, &power);
t[0]='d';
t[1]=0;
sprintf(dest, temp, power);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
/*{{{ c*/
case 'c':
{
int power;
mant_exp(1.0, x, 0, NULL, &power);
t[0]='c';
t[1]=0;
power = power / 3 + 6;
if (power<0 || power>12) power=12;
/* please extend the range ! */
sprintf(dest, temp, "**pnum kMG***"[power]);
dest += strlen(dest);
++format;
break;
}
/*}}}*/
default:
int_error("Bad format character", NO_CARET);
}
/*}}}*/
}
}
/*}}}*/
/*{{{ gen_tics*/
/* uses global arrays ticstep[], ticfmt[], min_array[], max_array[],
* auto_array[], log_array[], log_base_array[]
* we use any of GRID_X/Y/X2/Y2 and _MX/_MX2/etc - caller is expected
* to clear the irrelevent fields from global grid bitmask
* note this is also called from graph3d, so we need GRID_Z too
*/
void gen_tics(axis, def, grid, minitics, minifreq, callback)
int axis; /* FIRST_X_AXIS, etc */
struct ticdef *def; /* tic defn */
int grid; /* GRID_X | GRID_MX etc */
int minitics; /* minitics - off/default/auto/explicit */
double minifreq; /* frequency */
tic_callback callback; /* fn to call to actually do the work */
{
/* seperate main-tic part of grid */
int lgrd= (grid&(GRID_X|GRID_Y|GRID_X2|GRID_Y2|GRID_Z)) ? grid_linetype : -2;
int mgrd= (grid&(GRID_MX|GRID_MY|GRID_MX2|GRID_MY2|GRID_MZ)) ? mgrid_linetype : -2;
if (def->type==TIC_USER) { /* special case */
/*{{{ do user tics then return*/
struct ticmark *mark = def->def.user;
double uncertain=(max_array[axis]-min_array[axis])/10;
double ticmin=min_array[axis]-SIGNIF*uncertain;
double internal_max=max_array[axis]+SIGNIF*uncertain;
for (mark=def->def.user; mark; mark=mark->next) {
char label[64];
double internal = log_array[axis] ? log(mark->position)/log_base_array[axis] : mark->position;
if (!inrange(internal, ticmin, internal_max))
continue;
/* polar labels always +ve */
sprintf(label, mark->label ? mark->label : ticfmt[axis], polar ? fabs(mark->position):mark->position);
(*callback)(axis, internal, label, lgrd);
}
return; /* NO MINITICS FOR USER-DEF TICS */
}
/*}}}*/
/* series-tics
* need to distinguish user co-ords from internal co-ords.
* - for logscale, internal=log(user), else internal=user
*
* The minitics are a bit of a drag - we need to distinuish
* the cases step>1 from step==1.
* If step=1, we are looking at 1,10,100,1000 for example, so
* minitics are 2,5,8, ... - done in user co-ordinates
* If step>1, we are looking at 1,1e6,1e12 for example, so
* minitics are 10,100,1000,... - done in internal co-ords
*/
{
double tic; /* loop counter */
double internal; /* in internal co-ords */
double user; /* in user co-ords */
double start,step,end;
double lmin=min_array[axis], lmax=max_array[axis];
double internal_min, internal_max; /* to allow for rounding errors */
double ministart=0, ministep=1, miniend=1; /* internal or user - depends on step */
/* gprintf uses log10() of base - log_base_array is log() */
double log_base = log_array[axis] ? log10(base_array[axis]) : 1.0;
if (lmax < lmin) {
/* hmm - they have set reversed range for some reason */
double temp=lmin; lmin=lmax; lmax=temp;
}
/*{{{ choose start, step and end*/
switch (def->type) {
case TIC_SERIES:
if (log_array[axis]) {
/* we can tolerate start<=0 if step and end > 0 */
if (def->def.series.end <= 0 ||
def->def.series.incr <= 0)
return; /* just quietly ignore */
step=log(def->def.series.incr)/log_base_array[axis];
end=log(def->def.series.end)/log_base_array[axis];
start=def->def.series.start > 0 ?
log(def->def.series.start) / log_base_array[axis] :
step;
} else {
start=def->def.series.start;
step=def->def.series.incr;
end=def->def.series.end;
if (start==-VERYLARGE)
start=step*floor(lmin/step);
if (end == VERYLARGE)
end=step*ceil(lmax/step);
}
break;
case TIC_COMPUTED:
/* round to multiple of step */
start=ticstep[axis]*floor(lmin/ticstep[axis]);
step=ticstep[axis];
end=ticstep[axis]*ceil(lmax/ticstep[axis]);
break;
case TIC_MONTH:
start=floor(lmin);
end=ceil(lmax);
step=floor((end-start)/12);
if (step<1) step=1;
break;
case TIC_DAY:
start=floor(lmin);
end=ceil(lmax);
step=floor((end-start)/14);
if (step<1) step=1;
break;
default:
graph_error("Internal error : unknown tic type");
return; /* avoid gcc -Wall warning about start */
}
/*}}}*/
/*{{{ ensure ascending order*/
if (end < start) {
double temp;
temp=end; end=start; start=temp;
}
step=fabs(step);
/*}}}*/
if (minitics) {
/*{{{ figure out ministart, ministep, miniend*/
if (minitics==MINI_USER) {
/* they have said what they want */
if (minifreq<=0)
minitics=0; /* not much else we can do */
else {
ministart = ministep=step/minifreq;
miniend = step;
}
} else if (log_array[axis]) {
if (step>1.5) { /* beware rounding errors */
/*{{{ 10,100,1000 case*/
/* no more than five minitics */
ministart = ministep = (int) (0.2*step);
if (ministep < 1)
ministart = ministep = 1;
miniend = step;
/*}}}*/
} else {
/*{{{ 2,5,8 case*/
miniend = base_array[axis];
if (end-start >= 10)
minitics=0; /* none */
else if (end-start >= 5) {
ministart=2;
ministep=3;
} else {
ministart=2;
ministep=1;
}
/*}}}*/
}
} else if (datatype[axis]==TIME) {
ministart=ministep=make_ltic(timelevel[axis], step);
miniend=step*0.9;
} else if (minitics==MINI_AUTO) {
ministart=ministep=0.1*step;
miniend=step;
} else
minitics=0;
if (ministep<=0)
minitics=0; /* dont get stuck in infinite loop */
/*}}}*/
}
/*{{{ a few tweaks and checks*/
/* watch rounding errors */
end += SIGNIF*step;
internal_max=lmax + step*SIGNIF;
internal_min=lmin - step*SIGNIF;
if (step==0)
return; /* just quietly ignore them ! */
/*}}}*/
for (tic=start; tic<=end; tic+=step) {
/*{{{ calc internal and user co-ords*/
if (!log_array[axis]) {
internal = datatype[axis]==TIME ? time_tic_just(timelevel[axis],tic): tic;
user=CheckZero(internal,step);
} else {
/* log scale => dont need to worry about zero ? */
internal=tic;
user=pow(base_array[axis], internal);
}
/*}}}*/
if (internal > internal_max)
break; /* gone too far - end of series = VERYLARGE perhaps */
if (internal >= internal_min) {
/* continue; */ /* maybe minitics!!!. user series starts below min ? */
/*{{{ draw tick via callback*/
switch(def->type) {
case TIC_DAY : {
int d = (long)floor(user+0.5) % 7;
if (d<0) d+=7;
(*callback)(axis,internal, abbrev_day_names[d], lgrd);
break;
}
case TIC_MONTH: {
int m = (long)floor(user+0.5) % 12;
if (m<0) m+=12;
(*callback)(axis, internal, abbrev_month_names[m], lgrd);
break;
}
default: { /* comp or series */
char label[64];
if ( datatype[axis] == TIME ) {
/* If they are doing polar time plot, good luck to them */
gstrftime(label,24,ticfmt[axis],(double)user);
} else {
gprintf(label, ticfmt[axis], log_base, polar?fabs(user):user);
}
(*callback)(axis, internal, label, lgrd);
}
}
/*}}}*/
}
if (minitics) {
/*{{{ process minitics*/
double mplace, mtic;
for (mplace=ministart; mplace < miniend; mplace += ministep) {
if ( datatype[axis] == TIME )
mtic = time_tic_just(timelevel[axis]-1,internal+mplace);
else
mtic=internal + (log_array[axis]&&step<=1.5 ? log(mplace)/log_base_array[axis] : mplace);
if (inrange(mtic, internal_min, internal_max))
callback(axis, mtic, NULL, mgrd);
}
/*}}}*/
}
}
}
}
/*}}}*/
/*{{{ map_position*/
static void map_position(pos, x, y, what)
struct position *pos;
unsigned int *x, *y;
char *what;
{
switch (pos->scalex) {
case first_axes:
{
double xx=LogScale(pos->x, log_array[FIRST_X_AXIS], log_base_array[FIRST_X_AXIS], what, "x");
*x=xleft + (xx-min_array[FIRST_X_AXIS])*scale[FIRST_X_AXIS]+0.5;
break;
}
case second_axes:
{
double xx=LogScale(pos->x, log_array[SECOND_X_AXIS], log_base_array[SECOND_X_AXIS], what, "x");
*x=xleft + (xx-min_array[SECOND_X_AXIS])*scale[SECOND_X_AXIS]+0.5;
break;
}
case graph:
{
*x=xleft + pos->x*(xright-xleft) + 0.5;
break;
}
case screen:
{
register struct termentry *t = term;
*x = pos->x*(t->xmax) + 0.5;
break;
}
}
switch (pos->scaley) {
case first_axes:
{
double yy=LogScale(pos->y, log_array[FIRST_Y_AXIS], log_base_array[FIRST_Y_AXIS], what, "y");
*y=ybot + (yy-min_array[FIRST_Y_AXIS])*scale[FIRST_Y_AXIS]+0.5;
return;
}
case second_axes:
{
double yy=LogScale(pos->y, log_array[SECOND_Y_AXIS], log_base_array[SECOND_Y_AXIS], what, "y");
*y=ybot + (yy-min_array[SECOND_Y_AXIS])*scale[SECOND_Y_AXIS]+0.5;
return;
}
case graph:
{
*y=ybot + pos->y*(ytop-ybot) + 0.5;
return;
}
case screen:
{
register struct termentry *t = term;
*y = pos->y*(t->ymax) + 0.5;
return;
}
}
}
/*}}}*/